Two Pointers
String
Dynamic Programming
Description
Given a string s
and an integer k
, partition s
into k
substrings such that the letter changes needed to make each substring a semi-palindrome are minimized.
Return the minimum number of letter changes required.
A semi-palindrome is a special type of string that can be divided into palindromes based on a repeating pattern. To check if a string is a semi-palindrome:β
Choose a positive divisor d
of the string's length. d
can range from 1
up to, but not including, the string's length. For a string of length 1
, it does not have a valid divisor as per this definition, since the only divisor is its length, which is not allowed.
For a given divisor d
, divide the string into groups where each group contains characters from the string that follow a repeating pattern of length d
. Specifically, the first group consists of characters at positions 1
, 1 + d
, 1 + 2d
, and so on; the second group includes characters at positions 2
, 2 + d
, 2 + 2d
, etc.
The string is considered a semi-palindrome if each of these groups forms a palindrome.
Consider the string "abcabc"
:
The length of "abcabc"
is 6
. Valid divisors are 1
, 2
, and 3
.
For d = 1
: The entire string "abcabc"
forms one group. Not a palindrome.
For d = 2
:
Group 1 (positions 1, 3, 5
): "acb"
Group 2 (positions 2, 4, 6
): "bac"
Neither group forms a palindrome.
For d = 3
:
Group 1 (positions 1, 4
): "aa"
Group 2 (positions 2, 5
): "bb"
Group 3 (positions 3, 6
): "cc"
All groups form palindromes. Therefore, "abcabc"
is a semi-palindrome.
Example 1:
Input: s = "abcac", k = 2
Output: 1
Explanation: Divide s
into "ab"
and "cac"
. "cac"
is already semi-palindrome. Change "ab"
to "aa"
, it becomes semi-palindrome with d = 1
.
Example 2:
Input: s = "abcdef", k = 2
Output: 2
Explanation: Divide s
into substrings "abc"
and "def"
. Each needs one change to become semi-palindrome.
Example 3:
Input: s = "aabbaa", k = 3
Output: 0
Explanation: Divide s
into substrings "aa"
, "bb"
and "aa"
. All are already semi-palindromes.
Constraints:
2 <= s.length <= 200
1 <= k <= s.length / 2
s
contains only lowercase English letters.
Solutions
Solution 1
Python3 Java C++ Go TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 class Solution :
def minimumChanges ( self , s : str , k : int ) -> int :
n = len ( s )
g = [[ inf ] * ( n + 1 ) for _ in range ( n + 1 )]
for i in range ( 1 , n + 1 ):
for j in range ( i , n + 1 ):
m = j - i + 1
for d in range ( 1 , m ):
if m % d == 0 :
cnt = 0
for l in range ( m ):
r = ( m // d - 1 - l // d ) * d + l % d
if l >= r :
break
if s [ i - 1 + l ] != s [ i - 1 + r ]:
cnt += 1
g [ i ][ j ] = min ( g [ i ][ j ], cnt )
f = [[ inf ] * ( k + 1 ) for _ in range ( n + 1 )]
f [ 0 ][ 0 ] = 0
for i in range ( 1 , n + 1 ):
for j in range ( 1 , k + 1 ):
for h in range ( i - 1 ):
f [ i ][ j ] = min ( f [ i ][ j ], f [ h ][ j - 1 ] + g [ h + 1 ][ i ])
return f [ n ][ k ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 class Solution {
public int minimumChanges ( String s , int k ) {
int n = s . length ();
int [][] g = new int [ n + 1 ][ n + 1 ] ;
int [][] f = new int [ n + 1 ][ k + 1 ] ;
final int inf = 1 << 30 ;
for ( int i = 0 ; i <= n ; ++ i ) {
Arrays . fill ( g [ i ] , inf );
Arrays . fill ( f [ i ] , inf );
}
for ( int i = 1 ; i <= n ; ++ i ) {
for ( int j = i ; j <= n ; ++ j ) {
int m = j - i + 1 ;
for ( int d = 1 ; d < m ; ++ d ) {
if ( m % d == 0 ) {
int cnt = 0 ;
for ( int l = 0 ; l < m ; ++ l ) {
int r = ( m / d - 1 - l / d ) * d + l % d ;
if ( l >= r ) {
break ;
}
if ( s . charAt ( i - 1 + l ) != s . charAt ( i - 1 + r )) {
++ cnt ;
}
}
g [ i ][ j ] = Math . min ( g [ i ][ j ] , cnt );
}
}
}
}
f [ 0 ][ 0 ] = 0 ;
for ( int i = 1 ; i <= n ; ++ i ) {
for ( int j = 1 ; j <= k ; ++ j ) {
for ( int h = 0 ; h < i - 1 ; ++ h ) {
f [ i ][ j ] = Math . min ( f [ i ][ j ] , f [ h ][ j - 1 ] + g [ h + 1 ][ i ] );
}
}
}
return f [ n ][ k ] ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 class Solution {
public :
int minimumChanges ( string s , int k ) {
int n = s . size ();
int g [ n + 1 ][ n + 1 ];
int f [ n + 1 ][ k + 1 ];
memset ( g , 0x3f , sizeof ( g ));
memset ( f , 0x3f , sizeof ( f ));
f [ 0 ][ 0 ] = 0 ;
for ( int i = 1 ; i <= n ; ++ i ) {
for ( int j = i ; j <= n ; ++ j ) {
int m = j - i + 1 ;
for ( int d = 1 ; d < m ; ++ d ) {
if ( m % d == 0 ) {
int cnt = 0 ;
for ( int l = 0 ; l < m ; ++ l ) {
int r = ( m / d - 1 - l / d ) * d + l % d ;
if ( l >= r ) {
break ;
}
if ( s [ i - 1 + l ] != s [ i - 1 + r ]) {
++ cnt ;
}
}
g [ i ][ j ] = min ( g [ i ][ j ], cnt );
}
}
}
}
for ( int i = 1 ; i <= n ; ++ i ) {
for ( int j = 1 ; j <= k ; ++ j ) {
for ( int h = 0 ; h < i - 1 ; ++ h ) {
f [ i ][ j ] = min ( f [ i ][ j ], f [ h ][ j - 1 ] + g [ h + 1 ][ i ]);
}
}
}
return f [ n ][ k ];
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 func minimumChanges ( s string , k int ) int {
n := len ( s )
g := make ([][] int , n + 1 )
f := make ([][] int , n + 1 )
const inf int = 1 << 30
for i := range g {
g [ i ] = make ([] int , n + 1 )
f [ i ] = make ([] int , k + 1 )
for j := range g [ i ] {
g [ i ][ j ] = inf
}
for j := range f [ i ] {
f [ i ][ j ] = inf
}
}
f [ 0 ][ 0 ] = 0
for i := 1 ; i <= n ; i ++ {
for j := i ; j <= n ; j ++ {
m := j - i + 1
for d := 1 ; d < m ; d ++ {
if m % d == 0 {
cnt := 0
for l := 0 ; l < m ; l ++ {
r := ( m / d - 1 - l / d ) * d + l % d
if l >= r {
break
}
if s [ i - 1 + l ] != s [ i - 1 + r ] {
cnt ++
}
}
g [ i ][ j ] = min ( g [ i ][ j ], cnt )
}
}
}
}
for i := 1 ; i <= n ; i ++ {
for j := 1 ; j <= k ; j ++ {
for h := 0 ; h < i - 1 ; h ++ {
f [ i ][ j ] = min ( f [ i ][ j ], f [ h ][ j - 1 ] + g [ h + 1 ][ i ])
}
}
}
return f [ n ][ k ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 function minimumChanges ( s : string , k : number ) : number {
const n = s . length ;
const g = Array . from ({ length : n + 1 }, () => Array . from ({ length : n + 1 }, () => Infinity ));
const f = Array . from ({ length : n + 1 }, () => Array . from ({ length : k + 1 }, () => Infinity ));
f [ 0 ][ 0 ] = 0 ;
for ( let i = 1 ; i <= n ; ++ i ) {
for ( let j = 1 ; j <= n ; ++ j ) {
const m = j - i + 1 ;
for ( let d = 1 ; d < m ; ++ d ) {
if ( m % d === 0 ) {
let cnt = 0 ;
for ( let l = 0 ; l < m ; ++ l ) {
const r = ((( m / d ) | 0 ) - 1 - (( l / d ) | 0 )) * d + ( l % d );
if ( l >= r ) {
break ;
}
if ( s [ i - 1 + l ] !== s [ i - 1 + r ]) {
++ cnt ;
}
}
g [ i ][ j ] = Math . min ( g [ i ][ j ], cnt );
}
}
}
}
for ( let i = 1 ; i <= n ; ++ i ) {
for ( let j = 1 ; j <= k ; ++ j ) {
for ( let h = 0 ; h < i - 1 ; ++ h ) {
f [ i ][ j ] = Math . min ( f [ i ][ j ], f [ h ][ j - 1 ] + g [ h + 1 ][ i ]);
}
}
}
return f [ n ][ k ];
}
Solution 2
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 class Solution {
static int inf = 200 ;
List < Integer >[] factorLists ;
int n ;
int k ;
char [] ch ;
Integer [][] cost ;
public int minimumChanges ( String s , int k ) {
this . k = k ;
n = s . length ();
ch = s . toCharArray ();
factorLists = getFactorLists ( n );
cost = new Integer [ n + 1 ][ n + 1 ] ;
return calcDP ();
}
static List < Integer >[] getFactorLists ( int n ) {
List < Integer >[] l = new ArrayList [ n + 1 ] ;
for ( int i = 1 ; i <= n ; i ++ ) {
l [ i ] = new ArrayList <> ();
l [ i ] . add ( 1 );
}
for ( int factor = 2 ; factor < n ; factor ++ ) {
for ( int num = factor + factor ; num <= n ; num += factor ) {
l [ num ] . add ( factor );
}
}
return l ;
}
int calcDP () {
int [] dp = new int [ n ] ;
for ( int i = n - k * 2 + 1 ; i >= 1 ; i -- ) {
dp [ i ] = getCost ( 0 , i );
}
int bound = 0 ;
for ( int subs = 2 ; subs <= k ; subs ++ ) {
bound = subs * 2 ;
for ( int i = n - 1 - k * 2 + subs * 2 ; i >= bound - 1 ; i -- ) {
dp [ i ] = inf ;
for ( int prev = bound - 3 ; prev < i - 1 ; prev ++ ) {
dp [ i ] = Math . min ( dp [ i ] , dp [ prev ] + getCost ( prev + 1 , i ));
}
}
}
return dp [ n - 1 ] ;
}
int getCost ( int l , int r ) {
if ( l >= r ) {
return inf ;
}
if ( cost [ l ][ r ] != null ) {
return cost [ l ][ r ] ;
}
cost [ l ][ r ] = inf ;
for ( int factor : factorLists [ r - l + 1 ] ) {
cost [ l ][ r ] = Math . min ( cost [ l ][ r ] , getStepwiseCost ( l , r , factor ));
}
return cost [ l ][ r ] ;
}
int getStepwiseCost ( int l , int r , int stepsize ) {
if ( l >= r ) {
return 0 ;
}
int left = 0 ;
int right = 0 ;
int count = 0 ;
for ( int i = 0 ; i < stepsize ; i ++ ) {
left = l + i ;
right = r - stepsize + 1 + i ;
while ( left + stepsize <= right ) {
if ( ch [ left ] != ch [ right ] ) {
count ++ ;
}
left += stepsize ;
right -= stepsize ;
}
}
return count ;
}
}
GitHub