数据结构(kmp字符串匹配,trie存储字符串)

你还在使用for for的循环嵌套进行匹配嘛

你还在为因为一次匹配失误而从头再来而感到郁闷嘛

你还在o(nm)而烦恼嘛 

这就都不是问题,简单的kmp字符串匹配买不了吃亏买不了上当,马上让你一跃成为人生赢家,成为匹配王者(各种意义)

kmp字符串匹配

但当我们在进行字符串匹配的时候,让我们先想想朴素是怎么做的

string A,B;
int flag=0;
for(int i=0;i<A.size();i++)
{
    int memory=i;
    for(int j=0;j<B.size();j++)
    {
        if(i>=A.size())break;
        if(A[i]!=B[j])
        {
            i=memory;
            break;
        }
        if(j==B.size()-1){flag=1;}
        i++;
        
    }
    if(flag==1)
    {
        puts("good");
        break;
    }
}
if(flag==0)
{
    puts("bad");
}

 有人可能会说,这是不是太朴素了,为了推销而推销,我不否认,但是你要相信kmp真的快

我们通过观察可以发现,其实我们每一次匹配的时候,都已经匹配了很多了(红色是已经匹配成功的但是我们回退的时候是直接让指向待匹配的字符串(绿色)的下标直接回到头让后+1,而用来匹配的字符串(蓝色)直接回到头,在和+1的进行匹配

那么我们是不是可以少退一点呢?

我们发现黑色和黑色完全一样,所以我们回退就可以不用退完了,这样是不是很方便,而且我们思考的方向也有了,就是如何找到最长的一样的串,然后让自己的回退尽可能的少。也就是说我们的目标变为了如何才能找到尽可能小的回退路径。

下面是代码:

#include<iostream>

using namespace std;

const int N = 1000010;

char s[N],p[N];
int ne[N];

int main()
{
    int n,m;
    cin>>n >> p+1 >> m >>s+1;
    for(int i=2,j=0;i<=n;i++)//设置ne数组
    {
        while(j && p[j+1] != p[i] ) 
        {
            j = ne[j];
        }
        if( p[j+1] == p[i] )
        {
            
            j++;
        }
        ne[i]=j;
    }
    for(int i=1,j=0;i<=m;i++)
    {
        while(j && p[j+1]!=s[i] ) j=ne[j];//如果匹配成功,或者退无可退,才跳出while否则j要退到ne
        if( p[j+1]== s[i] )j++;//匹配成功j++
        
        if(j == n)//完全成功起飞
        {
            cout<<i-n<<" ";
            
            j=ne[j];//回退开始匹配下一个
        }
        
    }
    return 0;
}

 

Trie存储字符串

为什么要介绍这个呢?,因为用trie存储,可以让之后的查找变成o(n),十分的方便。

具体长什么样呢?

这样子就很清晰了,我们只需要根据需要存储的字符串的顺序,用链表进行存储就好了,其实很像另一种版本的邻接表,那么在做算法题的时候我们发现,一个一个去new太麻烦了,所以我们还是可以很快乐的用数组对其进行模拟。

#include<iostream>
#include<string>
using namespace std;
const int N = 10000;
int stn[N][26],cnt[N],idx;//stn的26是因为只有26个字母
void Insert(string s)
{   int q=0;
    for(int i=0;s[i];i++){
    int k=s[i]-'a';//把字符转化成对应下标
    if(!stn[q][k])stn[q][k]=++idx;//如果前面没有路就直接开条路走过去
    q=stn[q][k];
    }
    cnt[q]++;//说明这里存了一个字符串
}
int Find(string s)
{   
    int q=0;
    for(int i=0;i<s[i];i++){
    int k=s[i]-'a';
    if(!stn[q][k])return 0;//没找到返回0
    q=stn[q][k];
    }
    return cnt[q];//cnt[q]存了有多少个存在该点的字符串,有就是返回数量,没有就是0
}
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        char c;
        char str[N];
        cin>>c>>str;
        if(c=='I')Insert(str);
        else cout<<Find(str)<<endl;
    }
    return 0;
}

数据结构更多的还是一种思想,希望对大家有所帮助。

(kmp好像不是很清楚,欢迎反馈)

学习网站acwing

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值