字符串(KMP , Trie树 , STL)

kmp算法用于优化字符串匹配效率:

 

复制代码
//KMP字符串匹配: //模板: #include<bits/stdc++.h> using namespace std; const int N=1e6+10; char s1[N],s2[N]; int ne[N],n,res; int main() { std::ios::sync_with_stdio(false); cin>>s1+1>>s2+1; int len1=strlen(s1+1),len2=strlen(s2+1); for(int i=2,j=0;i<=len2;i++){ while(j&&s2[i]!=s2[j+1]) j=ne[j]; if(s2[i]==s2[j+1]) j++; ne[i]=j; } for(int i=1,j=0;i<=len1;i++){ while(j&&s1[i]!=s2[j+1]) j=ne[j]; if(s1[i]==s2[j+1]) j++; if(j==len2) cout<<i-j+1<<endl,j=ne[j]; } for(int i=1;i<=len2;i++) cout<<ne[i]<<" "; return 0; }
复制代码

 

复制代码
//test:https://www.luogu.com.cn/problem/CF25E #include<bits/stdc++.h> using namespace std; const int N=1e6+10,INF=2147483647; long long ne[4][N],len[4],k[4][4],res=INF;//ne是next数组,第i个的next数组,k是字符串是否匹配以及最大匹配的长度 char s[4][N]; bool vis[4]; void get_next(int l,char tmp[],int num)//预处理next数组,二维表示 { for(int i=2,j=0;i<=l;i++) { while(j&&tmp[i]!=tmp[j+1]) j=ne[num][j]; if(tmp[i]==tmp[j+1]) j++; ne[num][i]=j; } } int kmp(int len1,int len2,char s1[],char s2[],int num)//KMP处理匹配数据 { int j=0; for(int i=1;i<=len1;i++) { while(j&&s1[i]!=s2[j+1]) j=ne[num][j]; if(s1[i]==s2[j+1]) j++; if(j==len2){ return -1; j=ne[num][j]; } } return j; } int main() { std::ios::sync_with_stdio(false); cin>>s[1]+1>>s[2]+1>>s[3]+1; for(int i=1;i<=3;i++) { len[i]=strlen(s[i]+1); get_next(len[i],s[i],i); } for(int i=1;i<=3;i++){ for(int j=1;j<=3;j++) { if(i==j) continue; k[i][j]=kmp(len[i],len[j],s[i],s[j],j); } } for(int i=1;i<=3;i++) { for(int j=1;j<=3;j++) { for(int op=1;op<=3;op++)//分情况讨论 { if(i==j||j==op||i==op) continue; long long sum=len[i]+len[j]+len[op]-k[i][j]-k[j][op]; if(k[i][j]>=0&&k[j][op]>=0) //如果j和op都不是i的子串,那就是他们相加 res=min(res,sum); else { if(k[i][j]<0&&k[i][op]<0)//如果两者都是子串,那就是i的长度 res=min(res,len[i]); else if(k[i][j]<0)//如果只有j是子串,那就删去j res=min(res,len[i]+len[op]-k[i][op]); if(k[j][op]<0)//如果op是j的子串,那就删去op res=min(res,sum+k[j][op]-len[op]); } } } } cout<<res<<endl; return 0; }
复制代码

 

复制代码
//[BOI2009]Radio Transmission 无线传输:https://www.luogu.com.cn/problem/P4391 //对next数组更深刻的理解,如果周期性变化,那么n-next[n]就是最短周期 //因为next数组不可能储存第一个周期出现的次数,当地一个周期出现后,之后的都是以周期出现 //next数组会不断更新 #include<bits/stdc++.h> using namespace std; const int N=1e6+10; char s[N]; int ne[N],res,len,n; int main() { std::ios::sync_with_stdio(false); cin>>n>>s+1; for(int i=2,j=0;i<=n;i++){ while(j&&s[i]!=s[j+1]) j=ne[j]; if(s[i]==s[j+1]) j++; ne[i]=j; } cout<<n-ne[n]; return 0; }
复制代码
复制代码
//魔族密码:https://www.luogu.com.cn/problem/P1481 //字符串的最长上升子序列 //find函数返回的是第一个子串的第一个位置,如果没找到就是返回-1 //如果find==0,也就是从第一个字母开始就是匹配的,这种可以被分为一类 #include<bits/stdc++.h> using namespace std; const int N=2050; int n,res,f[N]; string s[N]; int main() { cin>>n; for(int i=0;i<n;i++) cin>>s[i]; for(int i=0;i<n;i++){ f[i]=1; for(int j=0;j<i;j++){ if(s[i]>s[j]&&s[i].find(s[j])==0) f[i]=max(f[i],f[j]+1); } } for(int i=0;i<n;i++) res=max(res,f[i]); cout<<res; return 0; }
复制代码

 

复制代码
//[POI2006] OKR-Periods of Words:https://www.luogu.com.cn/problem/P3435 //题意就是求每个子串的最小公共前后缀,也就是让我们的next数组缩到最小就可以 //既然要求最小公共前后缀,由于我们的next数组是求每一段子串最大的公共前后缀 //那么我们只需要不断的缩小next数组,直到next数组为0,为0的位置就是两者第一个公共前后缀的位置 //这里要记忆化一下,枚举到i的时候可以直接跳到j,减少枚举次数 #include <bits/stdc++.h> //#define int long long using namespace std; const int N=1e6+10,mod=1e9+7; string s; char s1[N]; long long n,t,a[N],f[N],res,num,ans,ne[N]; bool vis[N]; int main() { std::ios::sync_with_stdio(false); cin>>n>>s1+1; for(int i=2,j=0;i<=n;i++){ while(j&&s1[i]!=s1[j+1]) j=ne[j]; if(s1[i]==s1[j+1]) j++; ne[i]=j; } for(int i=1;i<=n;i++){ int j=i; while(ne[j]) j=ne[j]; cout<<i<<" "<<j<<endl; res+=i-j; if(ne[i]) ne[i]=j; } cout<<res; return 0; }
复制代码

 

 

 

Trie树: 0-1树

 

复制代码
//Trie树:高效查找和储存字符串,构成集合: #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int son[N][26],cnt[N],idx,n;//当前节点存的子节点,某个字母出现的次数,标记 void Insert(char str[])//插入树操作 { int p=0; for(int i=0;str[i];i++){ int u=str[i]-'a'; if(!son[p][u]) son[p][u]=++idx;//如果当前没有储存节点,那就建立一个节点 p=son[p][u];//向下移动 } cnt[p]++;//这个字符串+1; } int query(char str[]) { int p=0; for(int i=0;str[i];i++){ int u=str[i]-'a'; if(!son[p][u]) return 0;//如果没找到 p=son[p][u];//找到了,继续向下遍历 } return cnt[p]; } int main() { cin>>n; while(n--){ char op[2],s[N]; cin>>op>>s; if(op[0]=='I') Insert(s); else cout<<query(s)<<endl; } return 0; }
复制代码

 

复制代码
//于是他错误的点名开始了:https://www.luogu.com.cn/problem/P2580 #include<bits/stdc++.h> using namespace std; const int N=1e6+10; int son[N][26],idx,cnt[N],n,t; char s[N],tmp[N]; map<string,int>mp; void insert(char str[]) { int p=0; for(int i=0;str[i];i++){ int u=str[i]-'a'; if(!son[p][u]) son[p][u]=++idx; p=son[p][u]; } cnt[p]++; } bool check(char str[]) { int p=0; for(int i=0;str[i];i++){ int u=str[i]-'a'; if(!son[p][u]) return false; p=son[p][u]; } if(cnt[p]) return true;//借此判断 else return false; } int main() { cin>>n; for(int i=0;i<n;i++) cin>>s,insert(s); cin>>t; while(t--){ cin>>tmp; if(mp.count(tmp)) cout<<"REPEAT"<<endl; else{ if(check(tmp)){ cout<<"OK"<<endl; mp[tmp]=1; } else cout<<"WRONG"<<endl; } } return 0; }
复制代码
复制代码
//[USACO2.3]最长前缀 Longest Prefix:https://www.luogu.com.cn/problem/P1470 //方法一:KMP + dp //利用kmp匹配检查当前子串是否合法,最后枚举子串中的子串,确定最长子串 #include<bits/stdc++.h> using namespace std; const int N=3e5+10; string s="#",p[220]; int res,n,m,ne[220][15],len[220],c; bool vis[220][N],f[N];//vis代表i这个字符串的前j位是否合法,f代表长度i的字符串是否合法 void get_next(int num,string s2) { vis[num][1]=0; for(int i=2,j=0;i<len[num];i++){ while(j&&s2[i]!=s2[j+1]) j=ne[num][j]; if(s2[i]==s2[j+1]) j++; ne[num][i]=j; } } void KMP(string s1,int num,string s2) { for(int i=1,j=0;i<=s1.size();i++){ while(j&&s1[i]!=s2[j+1]) j=ne[num][j]; if(s1[i]==s2[j+1]) j++; if(j==len[num]){ vis[num][i]=true; j=ne[num][j]; } } } int main() { while(cin>>p[++c]&&p[c]!="."){ len[c]=p[c].length(); p[c]='#'+p[c];//向前添一位,从一开始 } c--; string x; while(cin>>x) s=s+x; for(int i=1;i<=c;i++){ get_next(i,p[i]); KMP(s,i,p[i]); } f[0]=true; for(int i=1;i<=s.size();i++){ for(int j=1;j<=c;j++) if(vis[j][i]&&(f[i]||f[i-len[j]])) f[i]=true; } for(int i=s.size();i>=0;i--) if(f[i]){ cout<<i; break; } return 0; } //方法二:set + dp //:规划的是s的状态,每次都看当前子串减去某一子串之后,该子串是否在集合中出现,是否合法 //f用于是不是合法,从前向后枚举每一子串 #include<bits/stdc++.h> using namespace std; const int N=1e6+10; int f[N],res,n,maxx; string ss,tmp=" "; set<string>s[20]; int main() { while(cin>>ss&&ss!="."){ s[ss.size()].insert(ss); maxx=max(maxx,(int)ss.size()); } while(cin>>ss) tmp=tmp+ss; f[0]=1; for(int i=1;i<tmp.size();i++){ for(int j=min(i,maxx);j>=1;j--){ string now=tmp.substr(i-j+1,j); if(s[now.size()].count(now)==1&&f[i-j]==1){ f[i]=1; res=i; break; } } } cout<<res; return 0; }
复制代码

 


__EOF__

本文作者Sakurajimamai
本文链接https://www.cnblogs.com/o-Sakurajimamai-o/p/17489715.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   o-Sakurajimamai-o  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
-- --
点击右上角即可分享
微信分享提示