#P2763. 第2题-小苯匹配字符串
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 31
            Accepted: 12
            Difficulty: 6
            
          
          
          
                       所属公司 : 
                              阿里
                                
            
                        
              时间 :2025年3月29日-阿里淘天(算法岗)
                              
                      
          
 
- 
                        算法标签>动态规划          
 
第2题-小苯匹配字符串
题解
题面描述
给定一个长度为 n 的 01 串 x(下标从 1 到 n),和另一个长度为 n−1 的 01 串 y(下标从 1 到 n−1)。串 y 用于约束串 x,具体规则如下:
- 如果 yi=1(1≤i≤n−1),则要求 xi=xi+1。
 - 如果 yi=0(1≤i≤n−1),则要求 xi=xi+1。
 
初始的串 x 可能不满足 y 的要求,允许通过将 x 中的某个字符取反(即执行 xi:=xi⊕1)来修改。目标是修改尽可能少的字符,使得最终的 x 串满足所有的约束条件。
思路
我们可以采用动态规划(DP)的方法求解。令 dp[i][b] 表示处理到 x 串的第 i 个位置,并且第 i 个字符修改后为 b(其中 b∈{0,1})时的最少修改次数。状态转移分为两种情况:
- 
对于第 1 个位置,由于没有前一个字符,所以直接比较原字符与目标字符是否相同即可。
- dp[1][0]=(x1==0?0:1)
 - dp[1][1]=(x1==1?0:1)
 
 - 
对于第 i 个位置(2≤i≤n),根据 yi−1 的值不同,有如下转移:
- 若 yi−1=0,则要求 xi−1 和 xi 必须相同。
- dp[i][b]=dp[i−1][b]+cost,其中 cost=(xi==b?0:1)。
 
 - 若 yi−1=1,则要求 xi−1 和 xi 必须不同。
- dp[i][b]=dp[i−1][1−b]+cost。
 
 
 - 若 yi−1=0,则要求 xi−1 和 xi 必须相同。
 
最后答案为 min(dp[n][0],dp[n][1])。
代码分析
整个算法的时间复杂度为 O(n),由于我们只维护前一个状态,可以考虑空间优化。算法核心在于根据 y 的约束决定 xi 与前一个位置 xi−1 是否取同样的值或者相反的值,并累计修改次数。状态转移时,每一步仅需要考虑两种可能,故整体实现十分高效。
C++
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
 
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin >> T;
    while(T--){
        int n;
        cin >> n;
        string x, y;
        cin >> x >> y;
        // dp[i][0] 表示前 i 个字符,且第 i 个字符为 '0' 时的最小修改次数
        // dp[i][1] 表示前 i 个字符,且第 i 个字符为 '1' 时的最小修改次数
        vector<vector<int>> dp(n+1, vector<int>(2, 1e9));
        // 初始化第 1 个字符
        dp[1][0] = (x[0] == '0' ? 0 : 1);
        dp[1][1] = (x[0] == '1' ? 0 : 1);
        for(int i = 2; i <= n; i++){
            // cost 表示将 x[i-1] 修改为目标字符 b 所需的代价
            for(int b = 0; b < 2; b++){
                int cost = (x[i-1] - '0' == b ? 0 : 1);
                if(y[i-2] == '0'){
                    // 当 y[i-1] 为 0 时,要求相等
                    dp[i][b] = dp[i-1][b] + cost;
                } else {
                    // 当 y[i-1] 为 1 时,要求不相等
                    dp[i][b] = dp[i-1][1-b] + cost;
                }
            }
        }
        int ans = min(dp[n][0], dp[n][1]);
        cout << ans << "\n";
    }
    return 0;
}
python
# 读取输入并处理多组测试数据
import sys
input = sys.stdin.readline
T = int(input())
for _ in range(T):
    n = int(input().strip())
    x = input().strip()
    y = input().strip()
    # dp[i][0] 表示前 i 个字符,且第 i 个字符为 '0' 的最小修改次数
    # dp[i][1] 表示前 i 个字符,且第 i 个字符为 '1' 的最小修改次数
    dp = [[10**9, 10**9] for _ in range(n+1)]
    # 初始化第 1 个字符
    dp[1][0] = 0 if x[0] == '0' else 1
    dp[1][1] = 0 if x[0] == '1' else 1
    for i in range(2, n+1):
        for b in range(2):
            # cost 表示将 x[i-1] 修改为字符 str(b) 所需的代价
            cost = 0 if x[i-1] == str(b) else 1
            if y[i-2] == '0':
                # y[i-1] 为 0 时,要求相等
                dp[i][b] = dp[i-1][b] + cost
            else:
                # y[i-1] 为 1 时,要求不相等
                dp[i][b] = dp[i-1][1-b] + cost
    ans = min(dp[n][0], dp[n][1])
    sys.stdout.write(str(ans) + "\n")
java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.StringTokenizer;
 
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        int T = Integer.parseInt(in.readLine());
        while(T-- > 0){
            int n = Integer.parseInt(in.readLine());
            String x = in.readLine();
            String y = in.readLine();
            // dp[i][0] 表示前 i 个字符,且第 i 个字符为 '0' 的最小修改次数
            // dp[i][1] 表示前 i 个字符,且第 i 个字符为 '1' 的最小修改次数
            int[][] dp = new int[n+1][2];
            int INF = 1000000000;
            for(int i = 0; i <= n; i++){
                dp[i][0] = INF;
                dp[i][1] = INF;
            }
            // 初始化第 1 个字符
            dp[1][0] = (x.charAt(0) == '0' ? 0 : 1);
            dp[1][1] = (x.charAt(0) == '1' ? 0 : 1);
            for(int i = 2; i <= n; i++){
                for(int b = 0; b < 2; b++){
                    // cost 表示将 x[i-1] 修改为字符 (char)(b + '0') 所需的代价
                    int cost = (x.charAt(i-1) == (char)(b + '0') ? 0 : 1);
                    if(y.charAt(i-2) == '0'){
                        // y[i-1] 为 0 时,要求相等
                        dp[i][b] = dp[i-1][b] + cost;
                    } else {
                        // y[i-1] 为 1 时,要求不相等
                        dp[i][b] = dp[i-1][1-b] + cost;
                    }
                }
            }
            int ans = Math.min(dp[n][0], dp[n][1]);
            System.out.println(ans);
        }
    }
}
        题目内容
小苯有一个长度为 n 的 01 串 x (下标从 1 到 n ),巧合的是格格也有一个长度恰好为 n−1 的 01 串 y。(下标从 1 到 n−1 )
据说,格格的字符串 y 是用来匹配小苯的字符串 x 的 ,具体来说:
- 
如果 yi=1(1≤i≤n−1),则意味着必须有:xi=xi+1。
 - 
如果 yi=0(1≤i≤n−1),则意味着必须有:xi=xi+1 。
 
而现在小苯的串 x 并不一定满足 y 串的匹配要求,因此格格希望小苯修改尽可能少的字符,使得匹配成立,请你帮小苯算一算至少需要修改多少个字符吧。
- 修改:选择 i(1≤i≤n),执行:xi:=xi⊕1。(其中 ⊕ 表示按位异或运算。)
 
输入描述
每个测试文件均包含多组测试数据。第一行输入一个整数 T(1≤T≤100) 代表数据组数,每组测试数据描述如下:
第一行输入一个正整数 n(2≤n≤106) 代表 x 串的长度。
第二行输入一个长度为 n,仅由字符 ′0’ 和 ′1’ 构成的字符串 x。
第三行输入一个长度为 n−1,仅由字符 ′0’ 和 ′1’ 构成的字符串 y。
除此之外,保证单个测试文件的 n 之和不超过 106 。
输出描述
对于每组测试数据:
在单独的一行输出一个整数,表示最少的修改次数,
样例1
输入
2
8
11001011
1000111
6
101010
11111
输出
4
0
说明
对于第一组测试数据,我们修改 x 串为:“10000101" 即可,需要至少 4 次修改操作。
对于第二组测试数据,我们不需要修改 x ,因此输出 0 即可。