洛谷 P3193
[HNOI2008] GT考试 思路 状态转移矩阵T[i][j]其实就是添加一个字符之后从状态i转移到状态j的方案数量,也就是先用kmp得到对应的T然后对于长度为N的串只需要让这个转移矩阵N次方就行了,然后Tn[0][j]就是子串最大匹配到j位的方案数,所求结果就是Tn[0][0]一直加到Tn[0][M-1](M是子串的长度)。 算法 KMP 自动机建模 + 矩阵快速幂 主要函数以及主要流程 矩阵相乘函数 矩阵快速幂函数(利用到矩阵相乘函数和快速幂算法) kmp算法(过于基础就不介绍了) 利用next函数得到基础转移矩阵T[m][n] 对T运用幂n函数得到Tn Tn按照思路直接操作就行了 题解(借鉴了AI,自己敲的,我太强了) #include<bits/stdc++.h> using namespace std; typedef long long ll; // 得到next函数 vector<int> getNext(string pattern) { int m = pattern.size(); vector<int> next(m,0); for(int i=1,j=0;i<m;i++) { while(j!=0 && pattern[i] != pattern[j]) j = next[j-1]; if(pattern[j] == pattern[i]) j++; next[i] = j; } return next; } // 矩阵相乘函数 vector<vector<int>> matMul(vector<vector<int>> a,vector<vector<int>> b,int K) { int m = a.size(); vector<vector<int>> res(m,vector<int>(m,0)); //结果 for(int i=0;i<m;i++) // i-k,k-j { for(int k=0;k<m;k++) { if(a[i][k]!=0) //剪枝减小工作量 { for(int j=0;j<m;j++) { res[i][j] = (res[i][j] + (ll)a[i][k] * b[k][j]) % K; } } } } return res; } // 矩阵幂函数(利用快速幂算法) vector<vector<int>> matPow(vector<vector<int>> a,int p,int K) { int m = a.size(); vector<vector<int>> res(m,vector<int>(m,0)); //结果 for(int i=0;i<m;i++) res[i][i] = 1; //单位矩阵用于起步 while(p!=0) { if(p & 1) res = matMul(res,a,K); a = matMul(a,a,K); p >>= 1; // p /= 2 } return res; } int main() { int N,M,K; // M<=20 N<=1e9 K<=10000,所以不能暴力解 cin >> N >> M >> K; string pattern; // 子串(此题中指的是不该出现的子串) cin >> pattern; // 求 pattern 的状态转移方程T[i][j] // 因为要用到pattern的next数组,所以先求next vector<int> next = getNext(pattern); vector<vector<int>> T(M,vector<int>(M,0)); // T[M][M],表示基础转移矩阵 for(int m=0;m<M;m++) // m为当前匹配数量,由于不准出现匹配到完整M的,所以最大长度为M-1 { for(int digit=0;digit<=9;digit++) // 尝试每一个数字,从而进行状态转移 { int cur = m; // 从当前位置开始回退 while(cur!=0 && (pattern[cur]-'0') != digit) cur = next[cur-1]; if((pattern[cur]-'0') == digit) cur++; if(cur < M) T[m][cur] = (T[m][cur] + 1) % K; } } // 得到完整的状态转移矩阵 vector<vector<int>> Tn = matPow(T,N,K); ll result = 0; for(int i=0;i<M;i++) { result = (result + Tn[0][i]) % K; } cout << result << endl; return 0; } 遇到问题 p>>1不会改变p的值应该是p>>=1 int类型乘法要注意防止溢出,一般用ll