版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a54665sdgf/article/details/81782769
题意:给你三个串s,a,b,让你在s中统计前缀为a后缀为b的不同子串的个数。
解法:将三个串的哈希值分别存起来,然后枚举所有的i,j,如果s[i-j]中前缀的哈希值与a相同且后缀的哈希值与b相同,则再检查s[i-j]的哈希值是否被访问过即可。可以先把所有满足要求的j预处理一下,速度可能会快很多。
用字符串hash的方法,决定速度的关键是访问过的hash值如何存储。用vis数组肯定是不行的,因为hash值是unsigned long long类型,数组开不到这么大。
经测试表明,存储hash值的方法,其速度大小排行为set<unordered_set<vector≈hash数组<hash表。
我的测试结果是:用set会超时,用unordered_set勉强过,用vector+sort的方法甚至比用unordered_set还快了两三倍,而且vector的速度和数组相差无几(也有可能是数据量大小的问题)。
最快的方法是用hash表,取字符串hash值的后几位作为数组下标,用链表的方式解决冲突,查询速度飞快。(unordered_set也是通过hash实现的,但速度远无法与手写的hash表相媲美......)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
#define FRER() freopen("i.txt","r",stdin)
#define FREW() freopen("o.txt","w",stdout)
using namespace std;
typedef unsigned long long ll;
const int N=2000+100;
const ll mod=(1<<22)-1;
char s[3][N];
int len[3],R[N],nR;
ll H[3][N],p=1e9+7,P[N];
int head[mod+1],nxt[N*N/2],nmem;
ll num[N*N/2];
ll Hash(int k,int l,int len)
{
return H[k][l+len]-H[k][l]*P[len];
}
bool Insert(ll x)
{
int H=x&mod;
for(int u=head[H]; ~u; u=nxt[u])
if(num[u]==x)
return false;
nxt[nmem]=head[H],num[nmem]=x,head[H]=nmem++;
return true;
}
int main()
{
//FRER();
memset(head,-1,sizeof head);
nmem=0;
P[0]=1;
for(int i=1; i<N; ++i)
P[i]=P[i-1]*p;
for(int i=0; i<3; ++i)
{
scanf("%s",s[i]);
len[i]=strlen(s[i]);
}
for(int k=0; k<3; ++k)
{
H[k][0]=0;
for(int i=1; i<=len[k]; ++i)
H[k][i]=H[k][i-1]*p+s[k][i-1];
}
ll Ha=Hash(1,0,len[1]);
ll Hb=Hash(2,0,len[2]);
nR=0;
for(int i=0; i+len[2]<=len[0]; ++i)
{
if(Hash(0,i,len[2])==Hb)
R[nR++]=i+len[2];
}
int cnt=0;
for(int i=0; i+len[1]<=len[0]; ++i)
{
if(Hash(0,i,len[1])==Ha)
{
for(int k=lower_bound(R,R+nR,i+max(len[1],len[2]))-R; k<nR; ++k)
if(Insert(Hash(0,i,R[k]-i)))++cnt;
}
}
printf("%d\n",cnt);
return 0;
}
//