题解 P4595 【[COCI2011-2012#5] POPLOCAVANJE】
因为是最优解就来写题解了(滑稽)
从数据大小上分析,这道题的
先考虑比较朴素的匹配,即将
但是显然这样的时间复杂度是
首先一个较明显的思路是对字符串的开头第一个字母进行分类,先将
这样就已经是第二优解了,但是这并不是最快的,我们还可以再每个大字符串的字符匹配时,从长度最大的字符串开始匹配,这样当我们匹配成功时,剩下的部分就没有必要在匹配了。
于是一个简单的哈希加差分加剪枝就能飞快地A掉这道黑题。
٩(๑❛ᴗ❛๑)۶
下面是我自己的代码。
ps:我使用的是对最后一个字符进行分类,这样能防止被卡,使代码更快。
#include<bits/stdc++.h>
#define un unsigned long long
using namespace std;
const int N=3e5+5;
struct z{
int Len;
un Has;
} g[30][5005];//将哈希值与长度装在结构体中方便排序和调用
int n,m,Len,cnt[30],L[N];
un f[N],num[N],p;
char a[N],s[5005];
bool cmp(z a,z b){
return a.Len>b.Len;
}
int main(){
scanf("%d%s",&n,&a);
num[0]=1;
for(int i=0;i<n;i++)f[i]=f[i-1]*233+a[i],num[i+1]=num[i]*233;
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%s",&s);
Len=strlen(s);
int mb=s[Len-1]-'a';//按照尾字母分类
p=0;
for(int j=0;j<Len;j++)p=p*233+s[j];
g[mb][++cnt[mb]]=(z){Len,p};
}
for(int i=0;i<26;i++)sort(g[i]+1,g[i]+cnt[i]+1,cmp);
for(int i=0;i<n;i++){
int mb=a[i]-'a';//按照尾字母进行查询
for(int j=1;j<=cnt[mb];j++){
Len=g[mb][j].Len;
if(i<Len-1)continue;
if(f[i]-f[i-Len]*num[Len]==g[mb][j].Has){
L[i-Len+1]++,L[i+1]--;//对覆盖区域进行差分
break;//剩下的都是比当前字符串短的,就算匹配成功对答案也没有贡献,可以直接break
}
}
}
int ans=0;
for(int i=0;i<n;i++){
if(i>0)L[i]+=L[i-1];
if(L[i]==0)ans++;
}
cout<<ans;
}