题目描述
输入描述:
输出描述:
示例1
输入
3
ab
ba
aba
输出
29
说明
题目大意
给定n个字符串,求所有字符串前缀与后缀相等的个数与前后缀的长度的平方的和。如样例,匹配长度为1,2,3的分别有4,4,1个,所以答案为
4 *1 ^2 +4 *2 ^2 +1 *3 ^2=29
分析
许多dalao们看到这题就会想到用map把所有的后缀(前缀)映射进去,然后对于每个前缀(后缀),就可以直接查找下就可以了。但是,map是pair和set合用,这全搞在一起是很慢的,看到1e6的长度,就是TLE。所以要找一个更加高深的算法。
HASH
哈希是字符串题目中的bug方法,基本上能水过去,实在不行还能骗不少分。本题中可以把后缀(前缀)的哈希值求出,然后将前缀(后缀)的哈希值与之比较,就能比较快捷地求出匹配了。但是哈希的进制搞成多少好呢?一般我们都是取一个素数进制,冲突的概率比较小。然而,本题有1e6的长度,以233进制的哈希为例,搞不好就是2331e6,炸上天。因此需要一个mod。但是这个mod之后,它也有概率会冲突。这就是看运气了。有一下3种比较好的解决这个办法。(后文采取第二种)
1、用大素数取模,这种冲突产生的概率就十分小了。
2、用unsigned long long,直接省略超出部分,概率小且写代码方便。
3、双哈希,这种方式就是取两种进制,然后都取一个mod,如果这都能冲突,那真可以买彩票了,唯一不足就是有点烦,蒟蒻不太喜欢这种写法。
以下是图示,是哈希的原理,即为进制转换,转换成233进制之类的。第一位的数值是2330 *当前位,以此类推。
具体代码见下。
void puthash(string a){
int len=a.size();
ull sum=0,p=1;//用ull解决冲突
for(int i=len-1;i>=0;i--){
sum+=(a[i]-'a'+1)*p;
p*=233;
mp[sum]++;
}//存后缀信息
}
KMP
如果你觉得这样就可以AC的话,那么你会喜提WA一枚。
来看下面这种情况。
如图,2,3串的匹配,是莫得问题的,然而,1串它先匹配了’a’,然后在判前缀为’aba’时又配了一次。但事实上,它只能匹配一次’aba’,所以要用到KMP里面的前缀函数nxt,快速找到最长的匹配串,然后把短的舍去。
具体写法如下(其实就是普通的KMP里的nxt)
void getnxt(string w){
int len=w.size(),k=-1;
nxt[0]=-1;
for(int i=1;i<len;i++){
while(k>-1&&w[k+1]!=w[i]) k=nxt[k];
if(w[k+1]==w[i]) k++;
nxt[i]=k;
}
}
代码
#include<bits/stdc++.h>
#define ll long long
#define inf 1<<30
#define ull unsigned long long
using namespace std;
const int M=1e5+100;
const int N=1e6+100;
const ull mod=998244353;
string s[M];//本来用了char,但是不用string的动态存储,这个会爆空间啊
int nxt[N],cur[N];
map<unsigned long long,int> mp;
void getnxt(string w){
int len=w.size(),k=-1;
nxt[0]=-1;
for(int i=1;i<len;i++){
while(k>-1&&w[k+1]!=w[i]) k=nxt[k];
if(w[k+1]==w[i]) k++;
nxt[i]=k;
}
}
void puthash(string a){
int len=a.size();
ull sum=0,p=1;
for(int i=len-1;i>=0;i--){
sum+=(a[i]-'a'+1)*p;
p*=233;mp[sum]++;
}
}
int main()
{
int n;
ll ans=0;
cin>>n;
for(int i=1;i<=n;i++)
cin>>s[i],puthash(s[i]);//存入后缀
for(int i=1;i<=n;i++){
int len=s[i].size();
ull t=0;
for(int j=0;j<len;j++){
t=t*233+(s[i][j]-'a'+1);
cur[j]=mp[t];
}//存入前缀信息
getnxt(s[i]);//得到前缀函数
for(int j=0;j<len;j++)
if(nxt[j]>=0) cur[nxt[j]]-=cur[j];//若有更长的的可以替换,则果断减掉
for(int j=0;j<len;j++){
ans+=cur[j]%mod*(j+1)%mod*(j+1)%mod;
ans%=mod;//按照题意存入答案,并mod
}
}
cout<<ans<<endl;
}