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

Posted 芜湖之肌肉金轮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构(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

以上是关于数据结构(kmp字符串匹配,trie存储字符串)的主要内容,如果未能解决你的问题,请参考以下文章

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

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

AC自动机初步

KMP算法详解以及Java代码实现

KMP算法详解以及Java代码实现

数据结构与算法简记--多模式字符串匹配AC自动机