原题链接:https://acm.ecnu.edu.cn/problem/3733/
前言:实际上如果当时可以回想起来hash这个事情的话,估计就做出来了,然后就进了前20(笑哭),tcl。今天晚上调了一个晚上,发现改成hash之后到处都是bugQWQ
分析:
可以发现由于等价具有传递性,当K>=7的情况下就可以完全不用特别处理了,答案就是C(N,2),因为这个时候所有的字都等价,那么所有的字符串都等价了。
对于K<=7的情况,可以发现实际上我们就是要根据题目K的限制把8个字符划分成若干个集合,集合内的字母等价。根据第二类斯特林数(后面会补一篇相关文章),可以发现总共的有意义状态数(也就是所有的K个等价条件都使用,并且等价条件使用不形成环)大概在5000以内,于是可以考虑一下枚举每种划分。
关于划分的枚举,考虑一个等价条件,实际上可以看成一条边,我们要有意义地利用所有的边,那么就是生成一棵树(或者说森林)。生成森林的话只要在递归过程中枚举每个点的父亲,并且保证父亲对应字符小于当前字符即可。
划分完成后,考虑字符串的对比。暴力对比会T,考虑hash,并优化。对于字符串进行预处理,即对字符串i,把其中的不同字符的hash值拆分开来算,求得i中某个字符变成另外某个字符的时候对应的对串i的hash值的贡献。最后每次生成划分之后比较字符串的时候只要通过O(8)的时间把预处理的内容组合起来就可以得到对应的正确hash值。
每次划分完成之后的计算时间复杂度是O(nlogn),可以通过此题。
(久了不怎么做题,这个题思路出来之后细节调了好久,对于字符统一化要找树的根的问题一开始还完全没有意识到)
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<set> 9 #include<map> 10 #include<vector> 11 #include<cctype> 12 using namespace std; 13 typedef long long LL; 14 const LL mo=1e15+13; 15 const int maxn=1005; 16 17 char S[maxn][maxn]; int N,M,K,ans; 18 int fa[10],rt[10]; 19 LL Hash[maxn][10][10],val[maxn];//hash[i][j][k] 表示第i个字符串中字符j转化为字符k的对应hash值 20 21 void data_in() 22 { 23 scanf("%d%d%d",&N,&M,&K); 24 for(int i=1;i<=N;i++) 25 scanf("%s",S[i]); 26 } 27 void ready() 28 { 29 for(int i=1;i<=N;i++) 30 for(int k=0;k<8;k++){ 31 LL tmp=1; 32 for(int j=0;j<M;j++){ 33 int jj=S[i][j]-'a'; 34 Hash[i][jj][k]=(Hash[i][jj][k]+tmp*k)%mo; 35 tmp=tmp*29%mo; 36 } 37 } 38 } 39 LL Hash_val(int i) 40 { 41 LL re=0; 42 for(int j=0;j<8;j++) 43 re+=Hash[i][j][rt[j]]; 44 return re%mo; 45 } 46 int root(int j) 47 { 48 while(fa[j]!=j) j=fa[j]; 49 return j; 50 } 51 void calc() 52 { 53 for(int j=0;j<8;j++) rt[j]=root(j); 54 for(int i=1;i<=N;i++) 55 val[i]=Hash_val(i); 56 sort(val+1,val+N+1); 57 int i=1,re=0; 58 while(i<=N){ 59 int cnt=1; 60 while(i<N&&val[i]==val[i+1]) i++,cnt++; 61 re+=cnt*(cnt-1)/2; 62 i++; 63 } 64 if(re>ans) ans=re; 65 } 66 void run(int i,int k) 67 { 68 if(i==8&&k==K){ calc(); return; } 69 if(i==8||k>K) return; 70 fa[i]=i; 71 run(i+1,k); 72 for(int j=0;j<i;j++){ 73 fa[i]=j; 74 run(i+1,k+1); 75 } 76 } 77 void work() 78 { 79 fa[0]=0; 80 if(K>=7) ans=N*(N-1)/2; 81 else{ 82 ready(); 83 run(1,0); 84 } 85 printf("%d\n",ans); 86 } 87 int main() 88 { 89 freopen("test.in","r",stdin); 90 freopen("test.out","w",stdout); 91 data_in(); 92 work(); 93 return 0; 94 }