week15选做-字典树-KMP

在这里插入图片描述

思路:
经典字典树
flag[i]代表第i个节点是否某为一个字符串的终点。
child[N][2]代表字典树,存储每一个节点的rank,child[i][0]==-1代表第i个节点的左子树为空,
对于每一个字符串,对每一个字符进行处理。
这是向字典树中插入字符的函数,即代码的精华部分。

int insert(string str){
  int crt=root;
  bool judge=0;
  int len=str.size();
  for(int i=0;i<len;i++){
   int x=str[i]-'0';
   if(child[crt][x]==-1){//遇到空值
    child[crt][x]=++tot;
    flag[tot]=0;
   }else{
    //在当前节点已被占用的情况下:
    //str是其他字符串的前缀||其他字符串是str的前缀 
    if(i==len-1||flag[child[crt][x]])
     judge=1;
   }
   crt=child[crt][x];
  }
  flag[crt]=1;
  return judge;
 }

完整代码:

#include<string.h>
#include<iostream>
#include<string>
using namespace std;
const int N=1010;
struct trie{
 int tot;
 int root;
 int child[N][2];
 int flag[N];
 trie(){
  memset(child,-1,sizeof(child));
  tot=0;
  root=0;
 }
 void clear(){
  memset(child,-1,sizeof(child));
  tot=0;
  root=0;
 }
 int insert(string str){
  int crt=root;
  bool judge=0;
  int len=str.size();
  for(int i=0;i<len;i++){
   int x=str[i]-'0';
   if(child[crt][x]==-1){//遇到空值 
    child[crt][x]=++tot;
    flag[tot]=0;
   }else{
    //str是其他字符串的前缀||其他字符串是str的前缀 
    if(i==len-1||flag[child[crt][x]])
     judge=1;
   }
   crt=child[crt][x];
  }
  flag[crt]=1;
  return judge;
 }
};
int main(){
 string ss;
 trie tri;
 int cas=1;
 bool judge=false;
 while(cin>>ss){
  if(ss!="9"){
   if(tri.insert(ss))
    judge=true;
  }
  else{
   if(!judge)
    cout<<"Set "<<cas<<" is immediately decodable"<<endl;
   else 
    cout<<"Set "<<cas<<" is not immediately decodable"<<endl;
   cas++;
   judge=false;
   tri.clear();
  }
 }
 return 0;
}

在这里插入图片描述
KMP算法:
找了很多博客,都写的乱七八糟,没有注释的代码和胡乱画的图,搞得我在kmp算法上浪费了很多时间。直到看到了这篇:
传送门-对kmp算法解析并模拟的博客
这篇博客写得真的好,是我见过的写得最清楚明白的原创博客,吹爆~
当然了,看完了不一定理解的话,需要自己手动模拟一遍。
关键代码:
求最长相同前后缀长度以及匹配过程:

//不加注释能直接看懂这段的都是大神!
/*
get_f(string)
求字符串的最长相同前后缀长度
从位置1(第二个字符)开始遍历
如:字符串aa,f[1]=1;f存储的是最长相同前后缀中前缀最后一个字符的位置
看不懂的可以手动模拟一下
*/
void get_f(char *x){
//f数组需要都初始化为-1 
 for(int i=1;i<m;i++){//每一次遍历求0~i的最长相同前后缀的长度 
  int j=f[i-1];//初始化为0~i-1部分的最长相同前后缀长度的位置
  while(x[j+1]!=x[i]&&j>=0){//j之前的部分就不用再匹配了
   j=f[j];
  }
  if(x[j+1]==x[i]){
   f[i]=j+1;
  }
  else{
   f[i]=-1;
  }
 }
}
void kmp(char *a,char *b){
 int i=0,j=0;
 int ans=0;
 while(i<n){
  if(a[i]==b[j]){
   i++;
   j++;
   if(j==m){
    ans++;
    j=f[j-1]+1;
    //已经匹配上,从f[m]+1的位置开始匹配就行
    //也就是从最长的相同前后缀+1的位置开始匹配
    //因为前面的部分都是一样的,不用再匹配 
   }
  }
  else{
   if(j==0){
    i++;
   } 
   else{
    j=f[j-1]+1;
    //中间没匹配上 
   }
  }
  //其实只要自己写个例子模拟一下,非常好理解,
  //单看代码很难理解。 
 }
 cout<<ans<<endl; 
}

完整代码:

#include<iostream>
#include<cstring> //卡了STL,这个string用不得
#include<cstdio>
using namespace std;
//string p,s;
int T;
int f[10010];
char p[10010];
char s[1000010];
int m;
int n;
void
void get_f(char *x){
 for(int i=1;i<m;i++){
  int j=f[i-1];
  while(x[j+1]!=x[i]&&j>=0){
   j=f[j]; 
  }
  if(x[j+1]==x[i]){
   f[i]=j+1;
  }
  else{
   f[i]=-1;
  }
 }
}
void kmp(char *a,char *b){
 int i=0,j=0;
 int ans=0;
 while(i<n){
  if(a[i]==b[j]){
   i++;
   j++;
   if(j==m){
    ans++;
    j=f[j-1]+1;
   }
  }
  else{
   if(j==0){
    i++;
   } 
   else{
    j=f[j-1]+1; 
   }
  }
 }
 cout<<ans<<endl; 
}
int main(){
 cin>>T;
 while(T--){
  memset(f,-1,sizeof(f));
  cin>>p;
  cin>>s;
  m=strlen(p);
  n=strlen(s);
  get_f(p);
  kmp(s,p);
 }
 return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值