您也可以在我的个人博客中阅读此文章:跳转
题意
一个字符串S 问其中有几个子串能满足以下条件:
1、长度为m*l
2、可以被分成m个l长的不同的子串
问题就变成了如何快速的判断着m个子串是否存在相同的
思路与分析
这题我做了两天,用了四种不同的方法,从TL到WA再到AC。。。一路坎坷。
不过收获了很多东西,这题价值很大,堪称经典。
运用滑动窗口的思想,长度为m*l的为大窗,将其划分为m个长度为l的小窗。
枚举大窗的起点,以l为单位滑动大窗
直接将所有的子串存储起来会MLE,所以想到hash
将总串s hash,然后想办法快速的取出hash[s[i]~s[j]]
最终算法如下:
- 大窗起点
- 计算当前大窗口下m个小窗的hash值,扔到map里
- 对map里的元素个数进行统计,若为m,ans++
- 以l为单位滑动大窗,每滑动一次,map减掉一个出窗口的l串,加上一个刚刚进窗口的l串,若此时map.size()==m,ans++
- 大窗起点+1,返回1
如何快速计算hash[s[i]~s[j]]
方法1
这里给出某大神的方法:
- base为基数,从S最后一个字符开始,hash[i]=hash[i+1]*base+str[i]-‘a’+1,即将i位以后的串hash成一个unsigned long long
- hash[s[i]~s[j]]的值即为: hash[i]-hash[j+1]*nbase[len].nbase[i]表示base的i次方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
struct HashTab{ ULL Has[1000005]; ULL base=31; ULL nbase[1000005]; void init(char *s){ int len=strlen(s); for (int i=len-1;i>=0;i--) { Has[i]=Has[i+1]*base+s[i]-'a'+1; } nbase[0]=1; for (int i=1;i<=len;i++) nbase[i]=nbase[i-1]*base; return; } ULL getHash(LL l,LL r){
|
方法2
没看懂,直接给出函数吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
struct Hash{ LL B,mod,len,Has[1000005],Base[1000005]; void init(char *s,LL _B,LL _mod){ len=strlen(s); B=_B; mod=_mod; Base[0]=1; Has[0]=0; for (LL i=1;i<=len;i++){ Base[i]=Base[i-1]*B%mod; Has[i]=(Has[i-1]*B+s[i-1]-'a'+1)%mod; } return; } LL getHash(LL l,LL r){
|
主代码
写的时候要注意下表是否正确哇,边界处到底有没有=号等等。。不然debug的时候是真头疼
方法1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
#include <iostream> #include <map> #include <cstring> #include <cstdio> #define ULL unsigned long long #define LL long long using namespace std; map <ULL,int> mp; struct HashTab{ ULL Has[1000005]; ULL base=31; ULL nbase[1000005]; void init(char *s){ int len=strlen(s); for (int i=len-1;i>=0;i--) { Has[i]=Has[i+1]*base+s[i]-'a'+1; } nbase[0]=1; for (int i=1;i<=len;i++) nbase[i]=nbase[i-1]*base; return; } ULL getHash(LL l,LL r){ int len=r-l+1; return Has[l]-Has[l+len]*nbase[len]; } }; HashTab _hash; int main () { int m,l; char s[100010]; while (scanf("%d%d",&m,&l)!=EOF){ scanf("%s",s); _hash.init(s); int len=strlen(s); int ans=0; for (int i=0;i<l&&i+m*l-1<len;i++){ mp.clear(); for (int j=i;j<i+m*l;j+=l){
|
方法2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
#include <iostream> #include <cstdio> #include <string> #include <cstring> #include <map> #define MOD 1000000007 #define LL long long using namespace std; map <long long,int> mp; struct Hash{ LL B,mod,len,Has[1000005],Base[1000005]; void init(char *s,LL _B,LL _mod){ len=strlen(s); B=_B; mod=_mod; Base[0]=1; Has[0]=0; for (LL i=1;i<=len;i++){ Base[i]=Base[i-1]*B%mod; Has[i]=(Has[i-1]*B+s[i-1]-'a'+1)%mod; } return; } LL getHash(LL l,LL r){ l++;r++; return ((Has[r]-Has[l-1]*Base[r-l+1])%mod+mod)%mod; } };
Hash _hash; int main () { int m,l; char s[100010]; while (scanf("%d%d",&m,&l)!=EOF){ scanf("%s",s); _hash.init(s,131,MOD); int len=strlen(s); int ans=0; for (int i=0;i<l&&i+m*l-1<len;i++){ mp.clear(); for (int j=i;j<i+m*l;j+=l){
|
参考
- 高神 http://www.acmtime.com/?p=579
- http://blog.csdn.net/houserabbit/article/details/25740235