leetcode2156
重点为反向滑动窗口和取余后的操作
题意:给你一个字符串s,只包含小写字母,a的值为1依次递增,从其中的长度为k的子串通过hash(s, p, m) = (val(s[0]) * p0 + val(s[1]) * p1 + ... + val(s[k-1]) * pk-1) mod
公式得到值为val,若val==hashValue,则这个子串符合条件,若有多个子串则输出第一个符合条件的子串
思路:首先看一眼范围字符串的长度子串长度为2*104,时间复杂度最多能到O(nlogn),power和mod为109,有可能要用到矩阵快速幂。
暴力做法为枚举,显然会超过时间复杂度为O(n2)
然后想到滑动窗口,每次移动一个格子,去头加尾,依次进行则复杂度为O(n),可以尝试,发现不知道怎么去头和加尾,因为你去头的话,后面的数都要除以power,如果是正常的没有mod当然可以,但是mod之后就会有偏差,这种方法行不通。
之后想到反向滑动窗口,去尾加头,去尾除去power不行,但是我可以乘上power,这样就不会产生偏差,加头就直接相加即可。
有以下公式
(a*b)%mod = ((a%mod)*(b%mod))%mod
(a+b)%mod = ((a%mod)+(b%mod))%mod
代码如下:
class Solution {
public:
string subStrHash(string s, int power, int mod, int k, int hashValue) {
int n = s.length(), pos = -1;
long long val = 0, p = 1;
for(int i = n-1; i > n-k; i--){
val = power*(val+s[i]-96)%mod;
p = p*power%mod;
}
for(int i = n-k; i >= 0; i--){
val = (val+s[i]-96)%mod; // 加头
if(val==hashValue) pos = i;
val = power*(val+mod-p*(s[i+k-1]-96)%mod)%mod; // 去尾
}
return s.substr(pos, k);
}
};