1316. 不同的循环子字符串
给你一个字符串 text ,请你返回满足下述条件的 不同 非空子字符串的数目:
可以写成某个字符串与其自身相连接的形式(即,可以写为 a + a,其中 a 是某个字符串)。 例如,abcabc 就是 abc 和它自身连接形成的。
示例 1:
输入:text = “abcabcabc” 输出:3 解释:3 个子字符串分别为 “abcabc”,“bcabca” 和 “cabcab”。
示例 2:
输入:text = “leetcodeleetcode” 输出:2 解释:2 个子字符串为 “ee” 和 “leetcodeleetcode” 。
提示:
1 <= text.length <= 2000 text 只包含小写英文字母。
解析
- 题目要求不同的循环字符串长度,因此可以枚举长度[2,n/2]。
- 需要用到set去重,但是将字符串加入set的复杂度为O(N),子串比较的复杂度也是O(N),因此需要用到字符串hash的技巧。
- 字符串hash,简单来讲就像要将字符串"123"转换为数字123一样,数字的每次进制为10,字符串的可以取26 (一般取大于26的质数,防止冲突)。
- 我们用pre[i]表示字符串[0:i]表示的hash,那么就可以根据pre将s[i:j]的hash表示出来。怎么表示呢?下边举个例子。
假如:字符串 s=“123”
- pre[0]=0
- pre[1]=pre[i-1]*10+s[0]-‘0’=1;
- pre[2]=pre[i-1]*10+s[1]-‘0’=12;
- pre[3]=pre[i-1]*10+s[2]-‘0’=123;
如果想获取子串 “23” 对应的值 对应字符串的下标分别为l=1,r=2 只需要用pre[3]-pre[1]*100=123-1*100=23(也就是pre[r+1]-pre[l]*base[r-l+1])。 这里的100由字符串的长度决定,可以用一个mul数组存放不同长度的字符串要乘的底数
- mul[0]=1
- mul[1]=10
- mul[2]=100
- mul[3]=1000
到这里我们就能将字符串转换为数字了,但是可以发现字符串如果过长的话正常会溢出,因此需要取余操作。
pre[i] = ((ll)pre[i - 1] * base + text[i - 1]) % mod;
mul[i] = (ll)mul[i - 1] * base % mod;
在通过hash = pre[r+1] - pre[l] * base[r-l+1] 获取hash值时, 有可能会使pre[l] * base[r-l+1]溢出, 因此,hash = pre[r+1] - pre[l] * base[r-l+1]%mod.
由于已经对pre数组元素取余操作, 因此 pre[r+1] - pre[l] * base[r-l+1]%mod* 可能为负数. 例如11%10=1,5%10=5,1-5<0。 所以,hash=( pre[r+1] - pre[l] * base[r-l+1]%mod + mod)*。
到这里就完整的得到了子串对应的hash值.
相同技巧题目
int gethash(int l,int r,vector<int>& pre,vector<int>& mul){
return (pre[r + 1] - (ll)pre[l] * mul[r - l + 1] % mod + mod) % mod;
}
code
typedef long long ll;
class Solution {
constexpr static int mod = (int)1e9 + 7;
public:
int base=31;
int res=0;
int gethash(int l,int r,vector<int>& pre,vector<int>& mul){
return (pre[r + 1] - (ll)pre[l] * mul[r - l + 1] % mod + mod) % mod;
}
void check(string s,int k,vector<int>& pre,vector<int>& mul){
int n=s.size();
unordered_set<int> se;
for(int i=0;i<n-2*k+1;i++){
int fir=gethash(i,i+k-1,pre,mul);
int lst=gethash(i+k,i+2*k-1,pre,mul);
if(fir==lst&&se.find(fir)==se.end()){
se.insert(fir);
res++;
}
}
}
int distinctEchoSubstrings(string text) {
int n=text.size();
if(n==1)
return 0;
int base = 31;
vector<int> pre(n + 1), mul(n + 1);
pre[0] = 0;
mul[0] = 1;
for (int i = 1; i <= n; ++i) {
pre[i] = ((ll)pre[i - 1] * base + text[i - 1]) % mod;
mul[i] = (ll)mul[i - 1] * base % mod;
}
for(int i=1;i<=n/2;i++){
check(text,i,pre,mul);
}
return res;
}
};
|