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;
}


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