CF 176B Word Cut

之前没做出这道题是因为缺少自己的分析。

别人给你提供了一些思路,如果你照着做下去觉得可以比较顺利,就继续;如果总觉得想不通,就及时丢掉,自己想办法做。


从这个图中我们可以发现一些规律:原串不能产生原串(先不考虑原串中有重复的),能产生其他n-1个串。

第0步,原串是1,其他都是0,总和sum为1;

第1步:原串产生其他串1,其他串产生原串0,原串为0,其他串都为1。即原串:sum – d[0], 其他i串:sum-d[i]。sum为2

第2步:原串产生其他串0,其他串产生原串1,原串为2,其他串都为1。即原串:sum – d[0], 其他i串:sum-d[i]。sum为4

第3步:原串产生其他串2,其他串产生原串1,原串为2,其他串都为3。即原串:sum – d[0], 其他i串:sum-d[i]。sum为8

……

根据这些,我们设dp[i][j]表示第i步第j个串有多少种,

状态转移方程:dp[i][j]= sum[i- 1]-dp[i - 1][j];

       最后我们只需枚举要求的串在所给串中有多少个匹配位置,就把匹配位置的个数加起来即可。

优化:由于求第i步时我们只用到第i-1步,因此可用二层滚动数组;出了原串,其余串的个数相同,我们可以只保存原串,和第一个串。因此dp[2][2]即可。复杂度为O(k)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define LL long long
const LL M = 1000000007;
LL dp[2][2], sum[2];
char b[1010], e[1010];
inline LL Mod(LL x)
{
    return (x + M) % M;
}
int main()
{
    //freopen("input.txt", "r", stdin);
    int K, k, len;
    scanf("%s %s %d", b, e, &K);
    k = 0;
    dp[k][0] = 1;
    sum[k] = 1;
    len = strlen(b);
    for(int i = 1; i <= K; i++){
        k ^= 1;
        for(int j = 0; j < 2; j++)
            dp[k][j] = Mod(sum[k ^ 1] - dp[k ^ 1][j]);
        sum[k] = Mod(dp[k][0] + (len - 1) * dp[k][1]);
    }
    LL ans = 0;
    for(int i = 0, j, l; i < len; i++){
        for(j = 0, l = i; j < len; j++, l = (l + 1) % len){
            if(b[l] != e[j]) break;
        }
        if(j >= len){
            ans = i ? Mod(ans + dp[k][1]) : Mod(ans + dp[k][0]);
        }
    }
    printf("%I64d\n", ans);
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章