字符串转换
给你两个长度都为 n 的字符串 s 和 t 。你可以对字符串 s 执行以下操作:
将 s 长度为 l (0 < l < n)的 后缀字符串 删除,并将它添加在 s 的开头。
比方说,s = 'abcd' ,那么一次操作中,你可以删除后缀 'cd' ,并将它添加到 s 的开头,得到 s = 'cdab' 。
给你一个整数 k ,请你返回 恰好 k 次操作将 s 变为 t 的方案数。
1. 动态规划 + 矩阵快速幂 + KMP算法
由于结果很大,考虑动态规划的状态转移
状态的转移需要知道字符串循环节的个数,使用KMP求s的循环节个数,这里拆环为链
最后由于迭代次数过多,这里需要使用矩阵快速幂
class Solution {
public:
const int MOD = 1e9 + 7;
int numberOfWays(string s, string t, long long k) {
int n = s.size();//长度决定可以移动的距离和操作数
//dp1[i]表示i次操作后等于t的方案数,dp1[i] = dp1[i-1]*(c-1) + dp2[i-1]*c
//dp2[i]表示i次操作后不等于t的方案数dp2[i] = dp1[i-1]*(n-c) + dp2[i-1]*(n-1-c)
//下面我们要求s中有多少个循环节c,然后运算迭代k次
int c = search(s+s.substr(0,n-1),t);//寻找对应字符串中t出现的次数
if(c==0) return 0;//无法通过操作得到
vector<vector<int>> matrix = {
{c-1,c},
{n-c,n-1-c}
};
matrix = fastpower(matrix,k);
return matrix[0][s!=t];
}
int search(string text,string pattern){
//KMP算法
int n = pattern.size();
vector<int> fail(n, -1);//记录的是上一个相同位置
for (int i = 1; i < n; ++i) {
int j = fail[i - 1];
while (j != -1 && pattern[j + 1] != pattern[i])
j = fail[j];
if (pattern[j + 1] == pattern[i])
fail[i] = j + 1;
}
//计算出现多少次
int cnt = 0; int idx = -1;//模板串下标
for(int i=0;i<text.size();i++){//遍历主串
while(idx!=-1&&pattern[idx+1]!=text[i])
idx = fail[idx]; //重置模板串下标
if(pattern[idx+1]==text[i]) idx++;
if(idx==pattern.size()-1){
cnt++;
idx = fail[idx];
}
}
return cnt;
}
vector<vector<int>> mutiply(vector<vector<int>>& left,vector<vector<int>> &right){
int m = left.size(); int n = right.size();
vector<vector<int>> res(m,vector<int>(m));
for(int i=0;i<m;i++) //遍历left 的 m行
for(int j=0;j<m;j++) //遍历right 的 m列
for(int k=0;k<n;k++) //遍历相乘的每一个数
res[i][j] = (res[i][j] + ((long long)left[i][k]*right[k][j])%MOD)%MOD;
return res;
}
vector<vector<int>> identify(int m){
vector<vector<int>> res(m,vector<int>(m));
for(int i=0;i<m;i++)
res[i][i] = 1;
return res;
}
vector<vector<int>> fastpower(vector<vector<int>> cur, long long n){
int m = cur.size();
vector<vector<int>> res = identify(m);//初始化单位矩阵
while(n){//进行快速幂运算
if(n&1) res = mutiply(res,cur);
cur = mutiply(cur,cur);//倍增
n>>=1;
}
return res;
}
};