Automattic\WooCommerce\Internal\EmailEditor\WCTransactionalEmails
WCEmailTemplateChangeSummary::lcs_matches
Compute LCS over two flattened record sequences with text similarity as a tiebreaker. Returns matched pairs as (core_index, post_index) tuples in increasing order on both axes.
Cardinality (number of name matches) is the primary criterion. When two alignments tie on cardinality — common on uniform block-name runs like paragraph × N — the diagonal score adds a tiny bonus proportional to the Jaccard word similarity of the two records' inner text. The bonus is bounded so it can never trade a name match for a similarity gain. Net effect: when the merchant edits an existing paragraph, the LCS pairs their edited version with core's original (high word overlap) instead of with an unrelated paragraph that happens to be in the right position.
Public so WCEmailTemplateSelectiveApplier{} can reuse the same matched-pair alignment when applying merchant choices. Internal-namespace bounded; not part of any external contract.
Method of the class: WCEmailTemplateChangeSummary{}
No Hooks.
Returns
Array
Usage
$result = WCEmailTemplateChangeSummary::lcs_matches( $a, $b ): array;
- $a(array) (required)
- .
- $b(array) (required)
- .
WCEmailTemplateChangeSummary::lcs_matches() WCEmailTemplateChangeSummary::lcs matches code WC 10.9.1
public static function lcs_matches( array $a, array $b ): array {
$n = count( $a );
$m = count( $b );
if ( 0 === $n || 0 === $m ) {
return array();
}
$dp = array_fill( 0, $n + 1, array_fill( 0, $m + 1, 0.0 ) );
for ( $i = 1; $i <= $n; $i++ ) {
for ( $j = 1; $j <= $m; $j++ ) {
$up = $dp[ $i - 1 ][ $j ];
$left = $dp[ $i ][ $j - 1 ];
if ( $a[ $i - 1 ]['name'] === $b[ $j - 1 ]['name'] ) {
$bonus = self::LCS_SIMILARITY_BONUS * self::similarity_score(
$a[ $i - 1 ]['inner_text'],
$b[ $j - 1 ]['inner_text']
);
// Must compare against `up` and `left` even when names
// match: the bonus on the diagonal can be smaller than
// bonuses already accumulated in `up` / `left`. Taking the
// diagonal unconditionally would discard a higher-scoring
// alignment found via a different path. Cardinality is
// preserved because the max bonus per match is far below
// 1.0, so the diagonal still wins whenever it adds a new
// name match.
$diagonal = $dp[ $i - 1 ][ $j - 1 ] + 1.0 + $bonus;
$dp[ $i ][ $j ] = max( $diagonal, $up, $left );
} else {
$dp[ $i ][ $j ] = max( $up, $left );
}
}//end for
}//end for
$pairs = array();
$i = $n;
$j = $m;
while ( $i > 0 && $j > 0 ) {
if ( $a[ $i - 1 ]['name'] === $b[ $j - 1 ]['name'] ) {
$bonus = self::LCS_SIMILARITY_BONUS * self::similarity_score(
$a[ $i - 1 ]['inner_text'],
$b[ $j - 1 ]['inner_text']
);
$diagonal_score = $dp[ $i - 1 ][ $j - 1 ] + 1.0 + $bonus;
if ( abs( $dp[ $i ][ $j ] - $diagonal_score ) < 1e-9 ) {
$pairs[] = array( $i - 1, $j - 1 );
--$i;
--$j;
continue;
}
}
if ( $dp[ $i - 1 ][ $j ] >= $dp[ $i ][ $j - 1 ] ) {
--$i;
} else {
--$j;
}
}
return array_reverse( $pairs );
}