这两天做了几道题,跑来总结总结。
这道题到手先是读傻了。思来想去不得正解。百度了一下看到字符串Hash,立马关掉。又是一个陌生的术语。
找了些资料后先是做了poj1200,上手了字符串Hash。换句话说是BKDRHash。
简单的介绍一下。
unsigned int BKDRHash(char * str){
unsigned int base = 31; //131 1331 13331 ...
unsigned int hash = 0;
while (*str){
hash = hash*base + (*str++);
}
return hash;
}
这个函数可以把一个字符串转化成一个数字。
先去感受一下吧。
当你对BKDRHash有了一定概念以后。进入正文。
----------------------------------------------------------------------------------------------------------------------
遍历开头,也就是前L个字符。
然后把每L个字符转换成一个数字保存在数组中。
问题就转化成为求一个数组中连续的不相同的长度为m的子串。
用一个小技巧预处理一下可以让字符转化的复杂度降为O(n)。
求字符串前i个字符的Hash值。得到num[i]。
那么 i- j 对应的字符串所代表的数字为 num[j]-num[i]*p[j-i+1];
p为base对自己的累乘。不明白就看代码,一目了然。
问题传化后见变成了一个简单计数问题了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
typedef unsigned __int64 ULL;
const int N = 1e5 + 10;
char s[N];
ULL num[N];
ULL p[N];
int m, l;
map<ULL, int> mp;
ULL base = 31;
ULL t[N];
int cnt;
int main(){
p[0] = 1;
for (int i = 1; i < N; i++)
p[i] = p[i - 1] * base;
while (~scanf("%d%d%s", &m, &l, s)){
int len = strlen(s);
int i, j;
ULL Hash = 0;
//mp.clear();
memset(num,0,sizeof(num));
for (i = 1; i <= len ; ++i){
num[i] = num[i - 1] * base + s[i-1] - 'a';
}
int ml = m*l;
int ans = 0;
for (i = 0; i < l; ++i){
int tmp = 0;
cnt = 0;
for (j = i + 1; j + l - 1 <= len; j += l){
ULL Hash = num[j + l - 1] - num[j - 1] * p[l];
t[cnt++] = Hash;
/*// cout << Hash << endl;
if (mp[Hash] == false)
tmp++;
else{
mp.clear();
if (tmp >= m){
ans += tmp - m + 1;
tmp = tmp%m;
j -= tmp*l;
tmp = 0;
}
else tmp = 1;
}
mp[Hash] = true;
}
if (tmp >= m)ans += tmp - m + 1;
// cout << endl;*/
}
mp.clear();
for (j = 0; j < cnt; j++){
if (mp.find(t[j]) == mp.end())
{
mp[t[j]] = j;
tmp++;
}
else if (j - mp[t[j]] + 1 > m){
mp[t[j]] = j;
tmp++;
}
else if(tmp>=m){
ans += tmp - m + 1;
j = mp[t[j]];
tmp = 0;
mp.clear();
}
else{
j = mp[t[j]];
tmp = 0;
mp.clear();
}
}
if (tmp >= m){
ans += tmp - m + 1;
}
}
printf("%d\n" , ans );
}
return 0;
}
/*
3 3
qwertyuiopasqwefghjkl
*/