字符串的一些算法(有时间就更新)
有关字符串的算法有点多,打算一个一个慢慢来。
KMP
这是一个十分神奇的字符串匹配算法,详细过程可在网上搜索。为什么说KMP神奇呢?因为它困扰了我很久很久。days?no,years!
我就是对这个算法始终有点模糊,学了忘,忘了学,今天又学了一遍有了印象,也写出来了模板,但是可能过了几天又忘了,这种感觉很难讲,我觉得应该要始终备着一个模拟数据来每次模拟一遍,要不然我会对这个算法一直有阴影的。
例题:GDOI2017 房屋购置
这道题是对KMP有阴影的另一个原因……,其实就是道裸题,但是我永远不会忘记那一天的痛~
#include <bits/stdc++.h> #define mp make_pair using namespace std; typedef long long ll; inline int read(){int s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} const int maxn = 400005; const int INF = 0x3f3f3f3f; char sss[21][maxn],s1[maxn],s2[maxn]; int a[21],p[maxn]; int n,m,i,len_s1,j,k,l,len_s2,y,z; int main() { n=read(); m=read(); for (i=1;i<=n;i++) { scanf("%s",sss[i]+1); a[i]=strlen(sss[i]+1); } while(m--) { scanf("%s%s",s1+1,s2+1); len_s1=strlen(s1+1); len_s2=strlen(s2+1); j=0; for (i=2;i<=len_s1;i++) { while (s1[i]!=s1[j+1] && j) j=p[j]; p[i]=(s1[i]==s1[j+1])?++j:j; } for (k=1;k<=n;k++) { j=0; for (i=1;i<=a[k];i++) { if (sss[k][i]=='#') continue; while (s1[j+1]!=sss[k][i] && j) j=p[j]; if (s1[j+1]==sss[k][i]) { j++; if (j!=len_s1) continue; y=i; l=len_s2; while (l) { if (sss[k][y]!='#') sss[k][y]=s2[l],l--; y--; } l+=len_s1-len_s2; while (l) { if (sss[k][y]!='#') sss[k][y]='#',l--; y--; } j=0; } } } } for (i=1;i<=n;i++) { for (j=1;j<=a[i];j++) if (sss[i][j]!='#') cout<<sss[i][j]; cout<<endl; } }
Manacher算法
回文串算法,这个就比较好写,详细过程可在网上搜索。
Codeforces 1326D1 or D2
a+b,a是前缀,b是后缀
1 若a为空或b为空,则直接做一次回文串算法,注意看是否符合前缀或后缀
2 若a和b都不为空,则先字符串前后进行依次匹配,直到不能匹配时,把中间的字符串再取出来重复1的操作
#include <bits/stdc++.h> #define mp make_pair using namespace std; typedef long long ll; inline int read(){int s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} const int maxn = 4000005; const int INF = 0x3f3f3f3f; int i,j,t,len,len1,len2,Len[maxn]; string ss,ans1,ans2,s; char tmp[maxn]; void Manacher(string s) { int i,mx=0,mid=0,x,Left,Right,len; len1=len2=0; tmp[0]='@'; tmp[1]='#'; len=s.size(); for (i=0;i<=2*len+3;i++)Len[i]=0; for (i=1;i<=len;i++) { tmp[2*i]=s[i-1]; tmp[2*i+1]='#'; } tmp[2*len+2]='\0'; for (i=1;tmp[i];i++) { if (i<mx) Len[i]=min(Len[2*mid-i],mx-i); else Len[i]=1; while (tmp[i-Len[i]]==tmp[i+Len[i]]) Len[i]++; if (Len[i]+i>mx) { mx=Len[i]+i; mid=i; } x=Len[i]-1; if (i%2==0) { x=(x-1)/2; if ((i>>1)-x==1) len1=max(len1,Len[i]-1); if ((i>>1)+x==len) len2=max(len2,Len[i]-1); } else { x>>=1; Left=(i-1)/2; Right=(i+1)/2; if (Left-x+1==1) len1=max(len1,Len[i]-1); if (Right+x-1==len) len2=max(len2,Len[i]-1); } } } int main() { t=read(); while (t--) { cin>>s; len=s.size(); ans1=ans2=""; Manacher(s); if (len1>len2) ans1=s.substr(0,len1); else ans1=s.substr(len-len2); i=0,j=len-1; while (i<j && s[i]==s[j]) ans2+=s[i],i++,j--; if (i<j) { ss=s.substr(i,j-i+1); len=ss.size(); Manacher(ss); if (len1>len2) ans2+=ss.substr(0,len1); else ans2+=ss.substr(len-len2); } ans2+=s.substr(j+1); if (ans1.size()>=ans2.size()) cout<<ans1<<endl; else cout<<ans2<<endl; } return 0; }
这里给我自己提个醒:永远别在子函数里面开大数组!永远别在子函数里面开大数组!永远别在子函数里面开大数组!
字符串hash
模板
#include <bits/stdc++.h> #define debug freopen("r.txt","r",stdin) #define mp make_pair #define ri register int using namespace std; typedef unsigned long long ull; typedef long long ll; typedef pair<int, int> pii; const int maxn = 1e5+5; const int INF = 0x3f3f3f3f; const int mod = 998244353; const int base = 233; inline ll read(){ll s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} ull hash[maxn]; ull pw[maxn]; void init() { pw[0]=1; for (ri i=1;i<=len;i++) pw[i]=pw[i-1]*base; } ull gethash(int l,int r) { return hash[r]-hash[l-1]*pw[r-l+1]; } int main() { init(); for (i=1;i<=n;i++) { scanf("%s",s[i]+1); len=strlen(s+1); } hash[0]=1; for (int i=1;i<=len;i++) hash[i]=hash[i-1]*base+(s[i]-'a'); cout<<gethash(?,?)<<endl; return 0; }
字典树trie
根+每一层26个字母组成
可实现功能:
第一:词频统计;第二: 前缀匹配;第三:去重
适用范围:数据量大,重复多,但是数据种类小可以放入内存
基本原理及要点:实现方式,节点孩子的表示方式
模板题洛谷P2580
#include <bits/stdc++.h> #define debug freopen("r.txt","r",stdin) #define mp make_pair #define ri register int using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 500010+100; const int N=500010+100; const int INF = 0x3f3f3f3f; const int mod = 998244353; inline ll read(){ll s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;} int n,m,i; char s[maxn]; ll ans; struct trie{ int a[N][26],cnt[N],t; void init(){ t=0; add(); } int add(){ memset(a[t],0,sizeof(a[t])); cnt[t]=0; return t++; } void insert(const char s[]){ int k=0; for(int i=0;s[i];i++){ int c=s[i]-'a'; //小写字母 if(!a[k][c])a[k][c]=add(); k=a[k][c]; //son[k]++; //如果要记录子树大小 } // cnt[k]++; } ll query(const char s[]){ ll k=0; for(int i=0;s[i];i++){ int c=s[i]-'a'; //小写字母 if(!a[k][c])return 0; k=a[k][c]; } if (!cnt[k]) { cnt[k]=1; return 1; } return 2; } }tree; int main() { n=read(); tree.init(); for (i=1;i<=n;i++) { scanf("%s",s); tree.insert(s); } m=read(); for (i=1;i<=m;i++) { scanf("%s",s); ans=tree.query(s); if (ans==0) cout<<"WRONG"<<endl; else if (ans==1) cout<<"OK"<<endl; else cout<<"REPEAT"<<endl; } return 0; }