链接:http://acm.hdu.edu.cn/showproblem.php?pid=4821
题意:给一个字符串,选m个长度为l的子串组成新的串,要求这m个子串互不相同,问有多少种组合。
字符串hash题目,以前没做过,做这道之前还用bkdrhash做了两道简单的题目,POJ1200和HDU1800。
用base数组记录乘了几个seed,base[i]表示seed^i,这个数组在之后计算子串hash值的时候会用到,先预处理一遍节省时间。
如果字符串从前往后hash,则hash[ i ] - hash[ i - l ] * base[ l ] 就是子串 [ i - l , i ] 的hash值,而从后往前hash的话 hash[ i ] - hash[ i + l ] * base[ l ] 就是子串 [ i , i + l ] 的hash值。
推导过程:以从前往后hash为例。假设字符串abab,子串长度2,则
i = 4时的hash值( ( ( (0+a)*seed+b ) * seed +a ) * seed + b ) ,
i - l = 2的hash值( (0+a)*seed+b ),乘base[2]之后 ( ( ( (0+a)*seed+b ) * seed ) * seed )
二者相减为a * seed + b,就是区间 [ i - l, i ] 对应字母 ab 的hash值。
之后按照每一点枚举m个l长度的子串,当他们hash值不同时就是一个结果。
#include<cstring> #include<string> #include<fstream> #include<iostream> #include<iomanip> #include<cstdio> #include<cctype> #include<algorithm> #include<queue> #include<map> #include<set> #include<vector> #include<stack> #include<ctime> #include<cstdlib> #include<functional> #include<cmath> using namespace std; #define PI acos(-1.0) #define MAXN 100010 #define eps 1e-7 #define INF 0x7FFFFFFF #define seed 131 typedef long long ll; typedef unsigned long long ull; char s[MAXN]; ull base[MAXN],Hash[MAXN]; map<ull,int> mp; int main(){ int m,l,i,len,ans; base[0] = 1; for(i=1;i<MAXN;i++) base[i] = base[i-1] * seed; while(scanf("%d%d",&m,&l)!=EOF){ scanf("%s",s); ans = 0; len = strlen(s); Hash[len] = 0; for(i=len-1;i>=0;i--){ Hash[i] = Hash[i+1] * seed + s[i] - 'a'; } for(i=0;i<l&&i+m*l<len;i++){ mp.clear(); for(int j=i;j<i+m*l;j+=l){ ull temp = Hash[j] - Hash[j+l] * base[l]; mp[temp]++; } if(mp.size()==m) ans++; for(int j=i+m*l;j+l<=len;j+=l){ ull temp = Hash[j-m*l] - Hash[j-(m-1)*l] * base[l]; mp[temp]--; if(!mp[temp]) mp.erase(temp); temp = Hash[j] - Hash[j+l] * base[l]; mp[temp]++; if(mp.size()==m) ans++; } } printf("%d\n",ans); } return 0; }
原文地址:http://blog.csdn.net/zzzz40/article/details/38073251