1. 程式人生 > 其它 >[kmp + 狀態機DP]

[kmp + 狀態機DP]

你現在需要設計一個密碼 S,S 需要滿足:

S 的長度是 N;
S 只包含小寫英文字母;
S 不包含子串 T;
例如:abc 和 abcde 是 abcde 的子串,abd 不是 abcde 的子串。

請問共有多少種不同的密碼滿足要求?

由於答案會非常大,請輸出答案模 109+7 的餘數。

輸入格式
第一行輸入整數N,表示密碼的長度。

第二行輸入字串T,T中只包含小寫字母。

輸出格式
輸出一個正整數,表示總方案數模 109+7 後的結果。

資料範圍
1≤N≤50,
1≤|T|≤N,|T|是T的長度。

輸入樣例1:
2
a
輸出樣例1:
625
輸入樣例2:
4
cbc
輸出樣例2:
456924

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 55, MOD = 1e9 + 7;

//f[i][j]:表示生成了前i個字元,和T已經匹配到第j個字元的方案數;
int f[N][N];
int n, m;

char str[N];


int main()
{
    cin >> n >> str + 1; //從1開始;
    
    m = strlen(str + 1);
    
    int ne[N] = {0};
    
    //求next[]陣列;
    for (int i = 2, j = 0; i <= n; i++)
    {
        while (j && str[i] != str[j+1]) j = ne[j];
        if (str[i] == str[j+1]) j++;
        ne[i] = j;
    }
    
    f[0][0] = 1;
    for (int i = 0; i < n; i++) //列舉前i個字元;
        for (int j = 0; j < m; j++) //列舉第i位密碼匹配到的子串位置都列舉一遍;
            for (int k = 'a'; k <= 'z'; k++) //即列舉第i+1個位位置上的字元;
            {
                int u = j; //做KMP匹配;
                while (u && k != str[u + 1]) u = ne[u];
                if (k == str[u+1]) u++;
                if (u < m) f[i+1][u] = (f[i+1][u] + f[i][j]) % MOD; 
            }

    int res = 0;
    for (int i = 0; i < m; i++) res = (res + f[n][i]) % MOD;
    
    cout << res << endl;
    return 0;
}