哈希表
字符串
计数
前缀和
题目描述
给你两个字符串 a
和 b
,二者均由小写字母组成。一步操作中,你可以将 a
或 b
中的 任一字符 改变为 任一小写字母 。
操作的最终目标是满足下列三个条件 之一 :
a
中的 每个字母 在字母表中 严格小于 b
中的 每个字母 。
b
中的 每个字母 在字母表中 严格小于 a
中的 每个字母 。
a
和 b
都 由 同一个 字母组成。
返回达成目标所需的 最少 操作数。
示例 1:
输入: a = "aba", b = "caa"
输出: 2
解释: 满足每个条件的最佳方案分别是:
1) 将 b 变为 "ccc",2 次操作,满足 a 中的每个字母都小于 b 中的每个字母;
2) 将 a 变为 "bbb" 并将 b 变为 "aaa",3 次操作,满足 b 中的每个字母都小于 a 中的每个字母;
3) 将 a 变为 "aaa" 并将 b 变为 "aaa",2 次操作,满足 a 和 b 由同一个字母组成。
最佳的方案只需要 2 次操作(满足条件 1 或者条件 3)。
示例 2:
输入: a = "dabadd", b = "cda"
输出: 3
解释: 满足条件 1 的最佳方案是将 b 变为 "eee" 。
提示:
1 <= a.length, b.length <= 105
a
和 b
只由小写字母组成
解法
方法一:计数 + 枚举
我们先统计字符串 $a$ 和 $b$ 中每个字母出现的次数,记为 $cnt_1$ 和 $cnt_2$。
然后考虑条件 $3$,即 $a$ 和 $b$ 中的每个字母都相同。我们只需要枚举最终的字母 $c$,然后统计 $a$ 和 $b$ 中不是 $c$ 的字母的个数,即为需要改变的字符个数。
然后考虑条件 $1$ 和 $2$,即 $a$ 中的每个字母都小于 $b$ 中的每个字母,或者 $b$ 中的每个字母都小于 $a$ 中的每个字母。对于条件 $1$,我们令字符串 $a$ 所有字符都小于字符 $c$,字符串 $b$ 所有字符都不小于 $c$,枚举 $c$ 找出最小的答案即可。条件 $2$ 同理。
最终的答案即为上述三种情况的最小值。
时间复杂度 $O(m + n + C^2)$,其中 $m$ 和 $n$ 分别为字符串 $a$ 和 $b$ 的长度,而 $C$ 为字符集大小。本题中 $C = 26$。
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 class Solution :
def minCharacters ( self , a : str , b : str ) -> int :
def f ( cnt1 , cnt2 ):
for i in range ( 1 , 26 ):
t = sum ( cnt1 [ i :]) + sum ( cnt2 [: i ])
nonlocal ans
ans = min ( ans , t )
m , n = len ( a ), len ( b )
cnt1 = [ 0 ] * 26
cnt2 = [ 0 ] * 26
for c in a :
cnt1 [ ord ( c ) - ord ( 'a' )] += 1
for c in b :
cnt2 [ ord ( c ) - ord ( 'a' )] += 1
ans = m + n
for c1 , c2 in zip ( cnt1 , cnt2 ):
ans = min ( ans , m + n - c1 - c2 )
f ( cnt1 , cnt2 )
f ( cnt2 , cnt1 )
return ans
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 class Solution {
private int ans ;
public int minCharacters ( String a , String b ) {
int m = a . length (), n = b . length ();
int [] cnt1 = new int [ 26 ] ;
int [] cnt2 = new int [ 26 ] ;
for ( int i = 0 ; i < m ; ++ i ) {
++ cnt1 [ a . charAt ( i ) - 'a' ] ;
}
for ( int i = 0 ; i < n ; ++ i ) {
++ cnt2 [ b . charAt ( i ) - 'a' ] ;
}
ans = m + n ;
for ( int i = 0 ; i < 26 ; ++ i ) {
ans = Math . min ( ans , m + n - cnt1 [ i ] - cnt2 [ i ] );
}
f ( cnt1 , cnt2 );
f ( cnt2 , cnt1 );
return ans ;
}
private void f ( int [] cnt1 , int [] cnt2 ) {
for ( int i = 1 ; i < 26 ; ++ i ) {
int t = 0 ;
for ( int j = i ; j < 26 ; ++ j ) {
t += cnt1 [ j ] ;
}
for ( int j = 0 ; j < i ; ++ j ) {
t += cnt2 [ j ] ;
}
ans = Math . min ( ans , t );
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 class Solution {
public :
int minCharacters ( string a , string b ) {
int m = a . size (), n = b . size ();
vector < int > cnt1 ( 26 );
vector < int > cnt2 ( 26 );
for ( char & c : a ) ++ cnt1 [ c - 'a' ];
for ( char & c : b ) ++ cnt2 [ c - 'a' ];
int ans = m + n ;
for ( int i = 0 ; i < 26 ; ++ i ) ans = min ( ans , m + n - cnt1 [ i ] - cnt2 [ i ]);
auto f = [ & ]( vector < int >& cnt1 , vector < int >& cnt2 ) {
for ( int i = 1 ; i < 26 ; ++ i ) {
int t = 0 ;
for ( int j = i ; j < 26 ; ++ j ) t += cnt1 [ j ];
for ( int j = 0 ; j < i ; ++ j ) t += cnt2 [ j ];
ans = min ( ans , t );
}
};
f ( cnt1 , cnt2 );
f ( cnt2 , cnt1 );
return ans ;
}
};
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 func minCharacters ( a string , b string ) int {
cnt1 := [ 26 ] int {}
cnt2 := [ 26 ] int {}
for _ , c := range a {
cnt1 [ c - 'a' ] ++
}
for _ , c := range b {
cnt2 [ c - 'a' ] ++
}
m , n := len ( a ), len ( b )
ans := m + n
for i := 0 ; i < 26 ; i ++ {
ans = min ( ans , m + n - cnt1 [ i ] - cnt2 [ i ])
}
f := func ( cnt1 , cnt2 [ 26 ] int ) {
for i := 1 ; i < 26 ; i ++ {
t := 0
for j := i ; j < 26 ; j ++ {
t += cnt1 [ j ]
}
for j := 0 ; j < i ; j ++ {
t += cnt2 [ j ]
}
ans = min ( ans , t )
}
}
f ( cnt1 , cnt2 )
f ( cnt2 , cnt1 )
return ans
}
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 function minCharacters ( a : string , b : string ) : number {
const m = a . length ,
n = b . length ;
let count1 = new Array ( 26 ). fill ( 0 );
let count2 = new Array ( 26 ). fill ( 0 );
const base = 'a' . charCodeAt ( 0 );
for ( let char of a ) {
count1 [ char . charCodeAt ( 0 ) - base ] ++ ;
}
for ( let char of b ) {
count2 [ char . charCodeAt ( 0 ) - base ] ++ ;
}
let pre1 = 0 ,
pre2 = 0 ;
let ans = m + n ;
for ( let i = 0 ; i < 25 ; i ++ ) {
pre1 += count1 [ i ];
pre2 += count2 [ i ];
// case1, case2, case3
ans = Math . min ( ans , m - pre1 + pre2 , pre1 + n - pre2 , m + n - count1 [ i ] - count2 [ i ]);
}
ans = Math . min ( ans , m + n - count1 [ 25 ] - count2 [ 25 ]);
return ans ;
}