KMP&&Tire树

目录

kmp算法

题目

代码

Tire树

题目 Tire字符串统计

代码


kmp算法

   kmp的本质:  kmp字符串的本质是通过寻找字符串内部的公共部分实现跳过一些不必要的比对,来减少时间的算法。

  作用:这时当子串匹配时出现无法匹配不成功的时候 此时只需要跳到以此字符为结尾的字符串的最大前缀的最后一个字符 然后接着比对就可以了。

    前缀:一串字符串 一第一个字符开始的长度小于等于n-1的所有连续子字符串都是原字符串的前缀

    后缀:一串字符串一最后一个字符结尾长度,长度小于等于n-1的所有连续子串都是原字符串的后缀

例:“abcd” 的前缀{"a","ab","abc"}  后缀{"d" ,"cd","bcd"}

  

kmp的核心时对于nex数组的求解:

    求nex 的过程和 其实可以看作就是i 后面的p串和i前面的前缀比对;
    从最小的求过去 i和j都在变化 最小的前缀和后缀比对 如果一直不相等 出现一个相等的那么也只可能有着一个 前缀后缀相等 此时记录 让j++变化
    然后接着原数组变化 相等的话 这两个也是唯一最大的相等前后缀  j++变化
     如果一直相等相等前后缀就会变得很长 一旦出现不嫌烦等的情况 此时j所指的也是p串的一部分前缀,i匹配相当于子串匹配中出现一部分不匹配的情况,而此时p中从1到i的nex已经求出来了,j小于i 这时候相当于求前缀的前缀  那么j=nex【j】 然后判断p【j+1】和p【i】 就又能知道期最大前缀了;
     虽然整体代码看上去和子串匹配很像但是内在逻辑差别很大。
     尤其nex[i]=j;这一步很重要,内在逻辑性很强。

题目

给定一个模式串 SS,以及一个模板串 PP,所有字符串中只包含大小写英文字母以及阿拉伯数字。

模板串 PP 在模式串 SS 中多次作为子串出现。

求出模板串 PP 在模式串 SS 中所有出现的位置的起始下标。

输入格式

第一行输入整数 NN,表示字符串 PP 的长度。

第二行输入字符串 PP。

第三行输入整数 MM,表示字符串 SS 的长度。

第四行输入字符串 SS。

输出格式

共一行,输出所有出现位置的起始下标(下标从 00 开始计数),整数之间用空格隔开。

代码

#include<iostream>
using namespace std;

int nex[100010];
char s[1000010], p[100010];
int main()
{
    
    int m,n;
   
    //求nex
    
    cin>>n>>p+1>>m>>s+1;
    for(int i=2,j=0;i<=n;i++)
    {
        while(j&&p[i]!=p[j+1]) j=nex[j];
        if(p[j+1]==p[i]) j++;
        nex[i]=j;//有疑问
        
    }
    
    //匹配
    for(int i=1,j=0;i<=m;i++)
    {
        while(j&&s[i]!=p[j+1]) j=nex[j];
        if(p[j+1]==s[i]) j++;
        if(j==n)
        {
            cout<<i-n<<' ';
            
            j=nex[j];
        }
    }
    
    return 0;
}

Tire树


  Trie 用于快速插入和查找字符串集合的数据结构

  个人体会

  Trie 存储结构是无序的 有时候不能太过追求了解代码运行时内部存储情况(可能是目前水平没有达到)了解代码内涵写出代码  看到了一个大佬的解释 
  Trie树 其实时利用不同字符串的相同前缀实现快速存储和查找
  二维数据存储 第一位区分不同前缀 第二维确定方向
  
    插入和查找操作都是枚举字符 判断前缀p是否存在 如果不存在则进行分配并将节点移动到分配好的节点上去

 // int son[][] 是子节点  idx 是当前指针

sort(char[] c)
{
    int p=0;
    for(int i=0;c[i];i++)
    {
       int u=c[i]-'a';
        if(!son[p][u]) son[p][u]=idx++;
        p=son[p][u];
    }
    connt[p]++;
}

que()
{
    
    int p=0;
    for(int i=0;c[i];i++)
    {
       int u=c[i]-'a';
        if(!son[p][u]) return  0;
        p=son[p][u];
    }
    return count[p];
    
}
```

题目 Tire字符串统计

维护一个字符串集合,支持两种操作:

  1. I x 向集合中插入一个字符串 xx;
  2. Q x 询问一个字符串在集合中出现了多少次。

共有 NN 个操作,输入的字符串总长度不超过 105105,字符串仅包含小写英文字母。

输入格式

第一行包含整数 NN,表示操作数。

接下来 NN 行,每行包含一个操作指令,指令为 I x 或 Q x 中的一种。

输出格式

对于每个询问指令 Q x,都要输出一个整数作为结果,表示 xx 在集合中出现的次数。

每个结果占一行。

代码

#include<iostream>
using namespace std;
string s[100010];
int main()
{
    int n;cin>>n;
    char o ;string op;
    int i=0;
    while(n--)
    {
        cin>>o>>op;
        
        if(o=='I')
        {
            s[i]=op;
            i++;
        }
        else  
        {
            int k=0;
            for(int j=0;j<i;j++){
                if(s[j]==op) k++;
            }
            cout<<k<<endl;
        }
        
    }
    return 0;
    
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值