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.
解题思路
比赛看到的数据范围就猜到是矩阵乘法加快速幂的做法,不过还是没做出来。
把某个后缀移到字符串的最前面所得到的结果,本质上就是把字符串看成一个环,然后选择某个间隙把环拆开(不能选择原字符串中第一个与最后一个字符的间隙),得到一条链。假设字符串的长度为,第一个字符是,那么通过一次操作得到结果就是在环中选择一个作为开头,然后依次选择下一个字符拼接,展开成新的字符串。可以发现无论操作了多少次,所有结果形成的环都是一样的。
我们先统计原来的中有多少个下标满足在操作一次后,以为开头的字符串与匹配。假设为,那么不满足的下标数量自然就是了。统计的方法很简单,由于涉及到环,先在后面再拼接一个,然后对长度为的拼接字符串跑一遍kmp,统计在下标中有多少个位置与匹配即可(代码中为了方便下标从开始)。
题目问恰好操作次后与匹配的方案数,等价于问操作了次后以原串中那个下标为开头的方案数是多少。所以我们可以先定义状态表示操作了次后,得到以那个下标为开头的字符串的方案数。可以从操作了次的状态转移过来,但又发现在的状态中,既可以从以那个下标为开头的字符串转移过来,也可以从另外的个下标为开头的字符串转移过来,所以我们再定义另外一个状态表示操作了次后,得到以那个下标为开头的字符串的方案数。因此就有状态转移方程
如果直接算的话时间复杂度是的,为此我们试一下能不能转换成矩阵乘法。我们尝试把上面的式子变成矩阵乘法的形式:
发现是可以的,因此定义,系数矩阵,那么就会有。根据矩阵乘法的结合律,那么就有。根据定义,表示没有任何操作时的状态,因此如果初始时,那么就有,否则。答案就是中的,用快速幂求即可。
为了统一成矩阵乘矩阵的形式,代码中把扩充成了的矩阵,具体计算如下:
AC代码如下,时间复杂度为:
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/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17691882.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效