Birthday Cake(双hash(具体模板)+逆元+排序)
swjtu—春季集训 - Virtual Judge (vjudge.net)
思路:
- 把2个字符串组合在一起,在平分,看是否相同,按照原顺,切割时候,会出现要补字母的情况
- 于是预处理 按照字符串长度排序,以此解决这个问题
- 匹配的时候就利用双哈希+map处理就行了,
- 注意 逆元的时候:快速腻,mod 双哈希是不同的mod,按照以往的写法容易写错。(这个错找了30min)
后记:
- 写双哈希的时候,直接让变量名称大写,比较好写和检擦。
- 按照进制的思想写hash,并且从左往右和从右往左写的时候是不一样的
- 从左往右 代表着 数组第 i 位 : 以他为末尾的hash值,一共有 i 位(i从1开始算), 弄的时候直接 ha[i] x ba +s[i+1],(不用-'a')
- 从右往左 代表着 数组第 i 位: 以他为开头到末尾的hash值, 弄的时候 ha[i+1+1]+ s[i+1]xpo[k], k为循环变量. (注意 i是 i+1 !!),开始的i 是leng-1;
- 1e9+7 -- 61, 1e9+9 -- 97 // 有规律的记 利用97

#include <bits/stdc++.h> using namespace std; #define ri register int #define M 400005 int n,m; struct dain{ string s; int val; bool operator <(const dain &t)const { return val<t.val; } }p[M]; long long ksn(long long a,long long b,int mod) { long long ans=1; while(b) { if(b&1) ans=ans*a%mod; b>>=1,a=a*a%mod; } return ans; } const int mod=1e9+7; const int MOD=1e9+9; long long ba=61; long long BA=97; long long po[M],PO[M]; long long inv[M],INV[M]; void init(){ po[0]=1;PO[0]=1; inv[0]=1;INV[0]=1; for(ri i=1;i<=4e5;i++) { po[i]=po[i-1]*ba%mod; inv[i]=ksn(po[i],mod-2,mod); PO[i]=PO[i-1]*BA%MOD; INV[i]=ksn(PO[i],MOD-2,MOD); } } long long val[M],VAL[M]; int main(){ ios::sync_with_stdio(false); cin.tie(0); cin>>n; for(ri i=1;i<=n;i++) { cin>>p[i].s; p[i].val=p[i].s.length(); } sort(p+1,p+1+n); init(); map<int,int> mp; map<int,int> MP; long long ans=0; for(ri ii=1;ii<=n;ii++) { for(ri i=0;i<p[ii].s.length();i++) // 真实i+1, { val[i+1]=(val[i]*ba%mod+p[ii].s[i])%mod; VAL[i+1]=(VAL[i]*BA%MOD+p[ii].s[i])%MOD; } long long ha=0,HA=0; for(ri i=p[ii].s.length()-1,j=0;i>=0;i--,j++) { ha=(ha+p[ii].s[i]*po[j]%mod)%mod; HA=(HA+p[ii].s[i]*PO[j]%MOD)%MOD; if(mp.count((ha-val[i]+mod)%mod*inv[i]%mod)&&MP.count((HA-VAL[i]+MOD)%MOD*INV[i]%MOD)) ans+=min(mp[(ha-val[i]+mod)%mod*inv[i]%mod],MP[(HA-VAL[i]+MOD)%MOD*INV[i]%MOD]); } mp[ha]++,MP[HA]++; } cout<<ans; }