题目链接:String
题目大意
给你一个字符串S,问你满足下面两个条件的子串有多少个?
- 子串的长度必须是M×L;
- M个长度为L的串必须互不相同,两个字符串不同是指任意一个位置不相同就算作不同。
数据范围
S长度不超过100000, 1≤M∗L≤1≤M∗L≤ S的长度。
解题思路
首先可以采用字符串Hash将字符串处理便于储存,字符串Hash我是在网上找的一个方法,实际上是找一个基数Base,然后采用Base进制,可以采用模一个大质数,也可以是直接采用unsigned long long 爆了后自动模。这里是采用 unsigned long long。此题可以记下前缀Hash值,之后如果要找区间[l,r]这个串的Hash值,只需要Hash[r] - Hash[l - 1] * Pow[r - l + 1]。这是因为对于一个几进制数,比如10进制的12345,现在我已经求得Hash[1] = 1, Hash[2] = 12, Hash[3] = 123, Hash[4] = 1234, Hash[5] = 12345,, 然后我想求[3,5]的Hash值,那么因为是10进制一眼可以知道是345,那么计算机只能通过Hash[5] - Hash[2] * Pow[3];而Pow[3]即10的3次方,即为12345 - 12 × 1000 = 345,因此,可以知道其他进制也是满足此公式。
处理完字符串的Hash之后,我首先想的是外层枚举i从 1~len−M∗L+11~len−M∗L+1, 然后内层枚举从i开始,M个长度为L的串是否有重复值,如果没有答案就++,之后就TLE,其实复杂度是 (len−M∗L+1)∗M(len−M∗L+1)∗M * map(log), TLE也确实正常。之后才知道i只需要枚举从1~L1~L就行,然后内层枚举所有的长度为L的子串,并将它们的Hash顺序存入数组,之后采用尺取法,每M个看是否有M个不同的元素,如果是则答案++,此复杂度就到了O(L∗Len/L)∗log(ULL)O(L∗Len/L)∗log(ULL)即为O(len∗log)O(len∗log)。
AC代码
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<string>
#include<map>
#include<algorithm>
using namespace std;
typedef unsigned long long ULL;
const int maxn = 100000;
const ULL base = 233ULL;
ULL Hash[maxn + 5], Pow[maxn + 5];
ULL num[maxn + 5];
map<ULL, int>vis;
char s[maxn + 5];
int M, L, tot, res;
ULL Get_Hash(int l, int r) {
return Hash[r] - Pow[r - l + 1] * Hash[l - 1];
}
void solve() {
int cnt = 0;
vis.clear();
for(int i = 1; i <= M; i++) {
if(!vis[num[i]])cnt++;
vis[num[i]]++;
}
if(cnt == M)res++;
int r = M + 1, l = 1;
while(r <= tot) {
vis[num[l]]--; if(vis[num[l]] == 0)cnt--; l++;
if(vis[num[r]] == 0)cnt++; vis[num[r]]++; r++;
if(cnt == M)res++;
}
}
int main() {
Pow[0] = 1;
for(int i = 1; i <= maxn; i++)Pow[i] = Pow[i - 1] * base;
while(~scanf("%d%d", &M, &L)) {
res = 0; scanf("%s", s + 1);
int len = strlen(s + 1);
Hash[0] = 1;
for(int i = 1; i <= len; i++)Hash[i] = Hash[i - 1] * base + (ULL)(s[i] - 'a' + 1);
for(int i = 1; i <= L; i++) {
tot = 0;
for(int j = i; j <= (len - L + 1); j += L)num[++tot] = Get_Hash(j, j + L - 1);
solve();
}
printf("%d\n", res);
}
return 0;
}
另外基数Base要大于s[i],即满足进制规则。