• luogu2679 子串


    题目大意

      有两个仅包含小写英文字母的字符串 A 和 B。现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一 个新的字符串,请问有多少种方案可以使得这个新串与字符串 B 相等?注意:子串取出 的位置不同也认为是不同的方案。对于所有 10 组数据:1≤n≤1000,1≤m≤200,1≤k≤m。

    题解

      本题的错误解法是定义状态$f(i, j, k)$为字符串$A$的前$i$个字符,字符串$B$的前$j$个字符已经匹配完,且已经分了$k$块时的方案最多为多少,状态转移为$f(i+1,j,k)+=f(i,j,k),若A_{i+1}=B_{j+1},则f(i+1,j+1,k)+=f(i,j,k),f(i+1,j+1,k+1)+=f(i,j,k)$。

      本算法错在我们没有记录$f(i,j,k)$中$i$有没有选在子串中,这使各类关于$k$的转移,如$f(i+1,j+1,k)+=f(i,j,k)$等,不成立。

      所以正确解法为定义状态$f(i,j,k,0)$表示第$i$位选在子串中,$f(i,j,k,1)$表示没有选,这样就有递归式:$f(i+1,j,k,0)+=f(i,j,k,0),若A_{i+1}=B_{i+1},f(i+1,j+1,k+1,1)+=f(i,j,k,0)$;$f(i+1,j,k,0)+=f(i,j,k,1),若A_{i+1}=B_{i+1},f(i+1,j+1,k,1)+=f(i,j,k,1),f(i+1,j+1,k+1,1)+=f(i,j,k,1)$。初始条件$f(i,0,0,0)=1$

    踩过的坑

    • 滚动数组每一次都要清空!
    • $j=0$时,只有$f(i,0,0,0)$有意义,其它都没有意义,所以$f(i,0,...)$不能向$f(i+1,0,...)$转移。
    • 最后输出结果的时候别忘了取模呀!
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    #define ll long long
    #define F(i, j, k, t) DP[(i) & 1][j][k][t]
    const int MAX_N = 1010, MAX_M = 210;
    const ll P = 1e9 + 7;
    char A[MAX_N], B[MAX_M];
    ll DP[2][MAX_M][MAX_M][2];
    int N, M, K;
    
    ll Dp()
    {
        A[0] = '*', B[0] = '-';
        for (int i = 0; i <= N; i++)
        {
            memset(DP[i + 1 & 1], 0, sizeof(DP[i + 1 & 1]));
        	F(i, 0, 0, 0) = 1;
        	for (int j = 0; j <= M; j++)
            	for (int k = 0; k <= K; k++)
            	{
            		//t = 1
            		if (j > 0)
            			F(i + 1, j, k, 0) = (F(i + 1, j, k, 0) + F(i, j, k, 1)) % P;
            		if (A[i + 1] == B[j + 1])
            		{
                		F(i + 1, j + 1, k, 1) = (F(i + 1, j + 1, k, 1) + F(i, j, k, 1)) % P;
                		F(i + 1, j + 1, k + 1, 1) = (F(i + 1, j + 1, k + 1, 1) + F(i, j, k, 1)) % P;
            		}
            		//t = 0
            		if (j > 0)
            			F(i + 1, j, k, 0) = (F(i + 1, j, k, 0) + F(i, j, k, 0)) % P;
            		if (A[i + 1] == B[j + 1])
                		F(i + 1, j + 1, k + 1, 1) = (F(i + 1, j + 1, k + 1, 1) + F(i, j, k, 0)) % P;
            	}
        }
        return (F(N, M, K, 0) + F(N, M, K, 1)) % P;
    }
    
    int main()
    {
        scanf("%d%d%d
    ", &N, &M, &K);
        scanf("%s", A + 1);
        scanf("%s", B + 1);
        printf("%lld
    ", Dp());
        return 0;
    }
    

      

  • 相关阅读:
    BigDecimal
    android sdk manager 无法更新,解决连不上dl.google.com的问题
    程序卡在 while(SPI_I2S_GetFlagStatus(W5500_SPI, SPI_I2S_FLAG_TXE) == RESET) 处
    获取本设备IP地址
    Xamarin Android 监听音量键(下)
    xamarin Android 监听音量键(上)
    最大子序列和
    2.找出单独出现的数字
    编程题常见输入格式处理方法
    二进制比较:破解 LuaJIT 加密脚本的一种新思路。直接修改,无需反编译
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9677439.html
Copyright © 2020-2023  润新知