String Transformation

String Transformation

You are given two strings s and t of equal length n. You can perform the following operation on the string s:

  • Remove a suffix of s of length l where 0 < l < n and append it at the start of s.

For example, let s = 'abcd' then in one operation you can remove the suffix 'cd' and append it in front of s making s = 'cdab'.

You are also given an integer k. Return the number of ways in which s can be transformed into t in exactly k operations.

Since the answer can be large, return it modulo 109 + 7 .

Example 1:

复制代码
Input: s = "abcd", t = "cdab", k = 2
Output: 2
Explanation: 
First way:
In first operation, choose suffix from index = 3, so resulting s = "dabc".
In second operation, choose suffix from index = 3, so resulting s = "cdab".

Second way:
In first operation, choose suffix from index = 1, so resulting s = "bcda".
In second operation, choose suffix from index = 1, so resulting s = "cdab".
复制代码

Example 2:

Input: s = "ababab", t = "ababab", k = 1
Output: 2
Explanation: 
First way:
Choose suffix from index = 2, so resulting s = "ababab".

Second way:
Choose suffix from index = 4, so resulting s = "ababab".

Constraints:

  • 2 <= s.length <= 5 * 105
  • 1 <= k <= 1015
  • s.length == t.length
  • s and t consist of only lowercase English alphabets.
 

解题思路

  比赛看到k的数据范围就猜到是矩阵乘法加快速幂的做法,不过还是没做出来。

  把某个后缀移到字符串的最前面所得到的结果,本质上就是把字符串看成一个环,然后选择某个间隙把环拆开(不能选择原字符串中第一个与最后一个字符的间隙),得到一条链。假设字符串的长度为n,第一个字符是s0,那么通过一次操作得到结果就是在环中选择一个si,i[1,n1]作为开头,然后依次选择下一个字符s(i+1)modn拼接,展开成新的字符串。可以发现无论操作了多少次,所有结果形成的环都是一样的。

  我们先统计原来的s中有多少个下标i满足在操作一次后,以si为开头的字符串与t匹配。假设为c,那么不满足的下标数量自然就是nc了。统计的方法很简单,由于涉及到环,先在s后面再拼接一个s,然后对长度为2n的拼接字符串跑一遍kmp,统计在下标0n1有多少个位置与t匹配即可(代码中为了方便下标从1开始)。

  题目问恰好操作k次后st匹配的方案数,等价于问操作了k次后以原串s中那c个下标为开头的方案数是多少。所以我们可以先定义状态f(i)表示操作了i次后,得到以那c个下标为开头的字符串的方案数。可以从操作了i1次的状态转移过来,但又发现在i1的状态中,既可以从以那c个下标为开头的字符串转移过来,也可以从另外的nc个下标为开头的字符串转移过来,所以我们再定义另外一个状态g(i)表示操作了i次后,得到以那nc个下标为开头的字符串的方案数。因此就有状态转移方程{f(i)=f(i1)(c1)+g(i1)cg(i)=f(i1)(nc)+g(i1)(nc1)

  如果直接算的话时间复杂度是O(k)的,为此我们试一下能不能转换成矩阵乘法。我们尝试把上面的式子变成矩阵乘法的形式:[f(i)g(i)]=[f(i1)g(i1)]×[c1nccnc1]

  发现是可以的,因此定义Fi=[f(i)g(i)],系数矩阵A=[c1nccnc1],那么就会有Fi=Fi1×A。根据矩阵乘法的结合律,那么就有Fk=F0×Ak。根据定义,F0表示没有任何操作时的状态,因此如果初始时s=t,那么就有F0=[10],否则F0=[01]。答案就是Fk中的f(k),用快速幂求F0×Ak即可。

  为了统一成矩阵乘矩阵的形式,代码中把Fi扩充成了2×2的矩阵,具体计算如下:[f(i)g(i)00]=[f(i1)g(i1)00]×[c1nccnc1]

  AC代码如下,时间复杂度为O(n+logk)

复制代码
 1 class Solution {
 2 public:
 3     int mod = 1e9 + 7;
 4     
 5     int numberOfWays(string s, string t, long long k) {
 6         int n = t.size();
 7         vector<int> ne(n + 1);
 8         for (int i = 2, j = 0; i <= n; i++) {
 9             while (j && t[j] != t[i - 1]) {
10                 j = ne[j];
11             }
12             if (t[j] == t[i - 1]) j++;
13             ne[i] = j;
14         }
15         string ss = s + s;
16         int c = 0;
17         for (int i = 1, j = 0; i <= n << 1; i++) {
18             while (j && t[j] != ss[i - 1]) {
19                 j = ne[j];
20             }
21             if (t[j] == ss[i - 1]) j++;
22             if (j == n) {
23                 if (i - n + 1 <= n) c++;
24                 j = ne[j];
25             }
26         }
27         vector<vector<int>> f({{s == t, s != t}, {0, 0}}), a({{c - 1, n - c}, {c, n - c - 1}});
28         function<void(vector<vector<int>>&, vector<vector<int>>&, vector<vector<int>>&)> mul = [&](vector<vector<int>> &c, vector<vector<int>> &a, vector<vector<int>> &b) {
29             vector<vector<int>> tmp(2, vector<int>(2));
30             for (int i = 0; i < 2; i++) {
31                 for (int j = 0; j < 2; j++) {
32                     for (int k = 0; k < 2; k++) {
33                         tmp[i][j] = (tmp[i][j] + 1ll * a[i][k] * b[k][j]) % mod;
34                     }
35                 }
36             }
37             c = tmp;
38         };
39         while (k) {
40             if (k & 1) mul(f, f, a);
41             mul(a, a, a);
42             k >>= 1;
43         }
44         return f[0][0];
45     }
46 };
复制代码

 

参考资料

  第 362 场力扣周赛:https://leetcode.cn/circle/discuss/DihZU2/view/rOzbbt/

posted @   onlyblues  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示