【LeetCode第362场周赛】8020.字符串转换 | 推导+矩阵快速幂+KMP | 困难

题目内容

原题链接

给定两个长度均为 n n n 的字符串 s s s t t t

每次选择一个长度为 l ( 0 < l < n ) l(0<l<n) l(0<l<n) s s s 的后缀字符串 s u f suf suf,将 s u i f suif suif s s s 中删除,并添加到 s s s 的最前面。

s ← s [ 0 : n − l ] + s [ n − l : n ] s\leftarrow s[0:n-l]+s[n-l:n] ss[0:nl]+s[nl:n]

现在要求这么操作恰好 m m m 次,问有多少种不同的操作方案。

答案对 1 0 9 + 7 10^9+7 109+7 取模。

数据范围

  • 2 ≤ n ≤ 5 ⋅ 1 0 5 2\leq n\leq 5\cdot 10^5 2n5105
  • 1 ≤ m ≤ 1 0 15 1\leq m\leq 10^{15} 1m1015
  • s s s t t t 均只包含小写字母

题解

剖析操作

首先可以手动模拟一些例子,可以发现无论操作多少次,得到的串必然是 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<n) ops=ss[i:i+n](0i<n)

动态规划

状态定义

定义 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=jn1f[i1][k]

初始化 f [ 0 ] [ 0 ] = 1 , f [ 0 ] [ j ] = 0 ( 1 ≤ j < n ) f[0][0]=1,f[0][j]=0(1\leq j<n) f[0][0]=1,f[0][j]=0(1j<n)

假设有 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]=tf[k][idx]

但这样转移显然是不满足空间时间的要求的,考虑如何优化。

可以发现的是:

  • f [ i ] [ 0 ] = ( ∑ k = 0 n − 1 f [ i − 1 ] [ k ] ) − f [ i − 1 ] [ 0 ] = ( n − 1 ) × f [ i − 1 ] [ 1 ] f[i][0]=(\sum\limits_{k=0}^{n-1} f[i-1][k]) - f[i-1][0]=(n-1)\times f[i-1][1] f[i][0]=(k=0n1f[i1][k])f[i1][0]=(n1)×f[i1][1]
  • f [ i ] [ j ] = ( ∑ k = 0 n − 1 f [ i − 1 ] [ k ] ) − f [ i − 1 ] [ j ] = ( n − 2 ) × f [ i − 1 ] [ 1 ] + f [ i − 1 ] [ 0 ] f[i][j]=(\sum\limits_{k=0}^{n-1} f[i-1][k])-f[i-1][j]=(n-2)\times f[i-1][1]+f[i-1][0] f[i][j]=(k=0n1f[i1][k])f[i1][j]=(n2)×f[i1][1]+f[i1][0],其中 1 ≤ j < n 1\leq j<n 1j<n

这意味着我们其实对于每一层只需要求两个值 f [ i ] [ 0 ] , f [ i ] [ 1 ] f[i][0],f[i][1] f[i][0],f[i][1] 即可。

即:

  • f [ i ] [ 0 ] = ( n − 1 ) × f [ i − 1 ] [ 1 ] f[i][0]=(n-1)\times f[i-1][1] f[i][0]=(n1)×f[i1][1]
  • f [ i ] [ 1 ] = ( n − 2 ) × f [ i − 1 ] [ 1 ] + f [ i − 1 ] [ 0 ] f[i][1]=(n-2)\times f[i-1][1]+f[i-1][0] f[i][1]=(n2)×f[i1][1]+f[i1][0]

但是这么做的时间复杂度为 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][pi1qi1]=[k1pi1+k2qi1k3pi1+k4qi1]=[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 i1 状态转移到 i i i 状态。

上述我们已经定义了

  • p i = ( n − 1 ) × q i − 1 p_i=(n-1)\times q_{i-1} pi=(n1)×qi1 ,故 k 1 = 0 , k 2 = n − 1 k_1=0,k_2=n-1 k1=0,k2=n1
  • q i = ( n − 2 ) × q i − 1 + p i − 1 q_i=(n-2)\times q_{i-1}+p_{i-1} qi=(n2)×qi1+pi1 ,故 k 3 = 1 , k 4 = n − 2 k_3=1, k_4=n-2 k3=1,k4=n2

实际的矩阵乘法形式为:

[ 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 n11 n2][pi1qi1]=[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 n11 n2][pm1qm1]=[0 n11 n2]2[pm2qm2]==[0 n11 n2]m[p0q0](p0=1,q0=0)

矩阵快速幂来计算 m a t r i x m matrix^m matrixm 即可。

KMP

最后,我们要统计哪些 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<j<=n
        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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值