原题链接
给定两个长度均为 n n n 的字符串 s s s 和 t t t 。
每次选择一个长度为 l ( 0 < l < n ) l(0
即 s ← s [ 0 : n − l ] + s [ n − l : n ] s\leftarrow s[0:n-l]+s[n-l:n] s←s[0:n−l]+s[n−l:n]
现在要求这么操作恰好 m m m 次,问有多少种不同的操作方案。
答案对 1 0 9 + 7 10^9+7 109+7 取模。
首先可以手动模拟一些例子,可以发现无论操作多少次,得到的串必然是 s s s 的一个长度为 n n n 的循环串。
令 s s = s + s ss=s+s ss=s+s
即操作若干次后的 o p s = s s [ i : i + n ] ( 0 ≤ i < n ) ops = ss[i:i+n](0\leq i
状态定义
定义 f [ i ] [ j ] f[i][j] f[i][j] 表示操作了 i i i 次后,串变成了 s s [ j : j + n ] ss[j:j+n] ss[j:j+n] 的方案数。
状态转移
f [ i ] [ j ] = ∑ k = 0 & k ≠ j n − 1 f [ i − 1 ] [ k ] f[i][j]=\sum\limits_{k=0\&k\neq j}^{n-1} f[i-1][k] f[i][j]=k=0&k=j∑n−1f[i−1][k]
初始化 f [ 0 ] [ 0 ] = 1 , f [ 0 ] [ j ] = 0 ( 1 ≤ j < n ) f[0][0]=1,f[0][j]=0(1\leq j
假设有 c n t cnt cnt 个下标 i d x idx idx ,满足 s s [ i d x : i d x + n ] = t ss[idx:idx+n]=t ss[idx:idx+n]=t ,则最终的答案为: ∑ s s [ i d x : i d x + n ] = t f [ k ] [ i d x ] \sum\limits_{ss[idx:idx+n]=t} f[k][idx] ss[idx:idx+n]=t∑f[k][idx]。
但这样转移显然是不满足空间时间的要求的,考虑如何优化。
可以发现的是:
这意味着我们其实对于每一层只需要求两个值 f [ i ] [ 0 ] , f [ i ] [ 1 ] f[i][0],f[i][1] f[i][0],f[i][1] 即可。
即:
但是这么做的时间复杂度为 O ( m ) O(m) O(m),考虑如何优化。
这里令 p i = f [ i ] [ 0 ] , q i = f [ i ] [ 1 ] p_i=f[i][0], q_i=f[i][1] pi=f[i][0],qi=f[i][1]
[ k 1 k 2 k 3 k 4 ] [ p i − 1 q i − 1 ] = [ k 1 p i − 1 + k 2 q i − 1 k 3 p i − 1 + k 4 q i − 1 ] = [ p i q i ] \left[ \begin{matrix} k_1 \ k_2 \\ k_3 \ k_4 \end{matrix} \right] \left[ \begin{matrix} p_{i-1} \\ q_{i-1} \end{matrix} \right]= \left[ \begin{matrix} k_1p_{i-1}+k_2q_{i-1} \\ k_3p_{i-1}+k_4q_{i-1} \end{matrix} \right]= \left[ \begin{matrix} p_{i} \\ q_{i} \end{matrix} \right] [k1 k2k3 k4][pi−1qi−1]=[k1pi−1+k2qi−1k3pi−1+k4qi−1]=[piqi]
这里定义了一个矩阵 [ k 1 k 2 k 3 k 4 ] \left[ \begin{matrix} k_1 \ k_2 \\ k_3 \ k_4 \end{matrix} \right] [k1 k2k3 k4]
然后矩阵乘法将 i − 1 i-1 i−1 状态转移到 i i i 状态。
上述我们已经定义了
实际的矩阵乘法形式为:
[ 0 n − 1 1 n − 2 ] [ p i − 1 q i − 1 ] = [ p i q i ] \left[ \begin{matrix} 0 \ n-1 \\ 1 \ n-2 \end{matrix} \right] \left[ \begin{matrix} p_{i-1} \\ q_{i-1} \end{matrix} \right]= \left[ \begin{matrix} p_{i} \\ q_{i} \end{matrix} \right] [0 n−11 n−2][pi−1qi−1]=[piqi]
[ p m q m ] = [ 0 n − 1 1 n − 2 ] [ p m − 1 q m − 1 ] = [ 0 n − 1 1 n − 2 ] 2 [ p m − 2 q m − 2 ] = ⋯ = [ 0 n − 1 1 n − 2 ] m [ p 0 q 0 ] ( p 0 = 1 , q 0 = 0 ) \left[ \begin{matrix} p_{m} \\ q_{m} \end{matrix} \right]= \left[ \begin{matrix} 0 \ n-1 \\ 1 \ n-2 \end{matrix} \right] \left[ \begin{matrix} p_{m-1} \\ q_{m-1} \end{matrix} \right] = \left[ \begin{matrix} 0 \ n-1 \\ 1 \ n-2 \end{matrix} \right]^2 \left[ \begin{matrix} p_{m-2} \\ q_{m-2} \end{matrix} \right]=\cdots= \left[ \begin{matrix} 0 \ n-1 \\ 1 \ n-2 \end{matrix} \right]^{m} \left[ \begin{matrix} p_{0} \\ q_{0} \end{matrix} \right] (p_0=1,q_0=0) [pmqm]=[0 n−11 n−2][pm−1qm−1]=[0 n−11 n−2]2[pm−2qm−2]=⋯=[0 n−11 n−2]m[p0q0](p0=1,q0=0)
矩阵快速幂来计算 m a t r i x m matrix^m matrixm 即可。
最后,我们要统计哪些 i d x idx idx 满足 s s [ i d x : i d x + n ] = t ss[idx:idx+n]=t ss[idx:idx+n]=t ,这部分用 KMP 统计即可。
时间复杂度: O ( n + 8 log m ) O(n+8\log m) O(n+8logm) ,KMP 算法和矩阵快速幂的复杂度。
class Solution {
public:
static constexpr int MOD = 1e9 + 7;
vector<vector<int>> mul(const vector<vector<int>>& A, const vector<vector<int>>& B) {
int row = A.size(), col = B.size(), mid = A[0].size();
vector<vector<int>> res = {{0, 0}, {0, 0}};
for (int c = 0; c < mid; ++c)
for (int i = 0; i < row; ++i)
for (int j = 0; j < col; ++j)
res[i][j] = (1ll * A[i][c] * B[c][j] % MOD + res[i][j]) % MOD;
return res;
}
vector<vector<int>> qp(vector<vector<int>>& A, long long b) {
vector<vector<int>> res = {
{1, 0},
{0, 1}
};
while (b > 0) {
if (b & 1) res = mul(res, A);
A = mul(A, A);
b >>= 1;
}
return res;
}
pair<int, int> KMP(const string& a, const string& b) {
// 末尾添加空格,就不用判断越界了
string s = " " + a + a + " ";
string p = " " + b + " ";
int n = b.size();
int m = a.size() * 2;
// 寻找 p[1,n] 在 s[1,2m] 中存在的所有索引
// res.first 表示 s[1,n]=p[1,n] 的数量,要么为0,要么为1
// res.second 表示 s[j:j+n]=p[1,n] 的数量,1
pair<int, int> res = {0, 0};
vector<int> ne(n + 10);
for (int i = 2, j = 0; i <= n; ++i) {
while (j > 0 && p[j + 1] != p[i]) j = ne[j];
if (p[i] == p[j + 1]) j += 1;
ne[i] = j;
}
// 这里细节是 i < m,这样就不会让 s[n+1,2n] 被计算
for (int i = 1, j = 0; i < m; ++i) {
while (j > 0 && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j += 1;
if (j == n) {
if (i == n) res.first += 1;
else res.second += 1;
}
}
return res;
}
int numberOfWays(string s, string t, long long k) {
int n = s.size();
vector<vector<int>> matrix = {
{0, n - 1},
{1, n - 2}
};
int p0 = 1, q0 = 0;
auto res = qp(matrix, k);
int pk = (1ll * p0 * res[0][0] % MOD + 1ll * q0 * res[0][1] % MOD) % MOD;
int qk = (1ll * p0 * res[1][0] % MOD + 1ll * q0 * res[1][1] % MOD) % MOD;
// KMP 计算满足条件的 idx 的数量
auto cnt = KMP(s, t);
int ans = (1ll * cnt.first * pk % MOD + 1ll * cnt.second * qk % MOD) % MOD;
return ans;
}
};