其他分享
首页 > 其他分享> > 字符串学习笔记

字符串学习笔记

作者:互联网

一、字符串哈希

定义

字符串哈希实质上就是把每个不同的字符串转成不同的整数
这样相对于存储整个字符串来说占用的空间更少,而且也便于比较

实现

我们可以把每一个字符想象成一个数字,然后确立一个进制\(bas\)
比如一个字符串\(abc\)
我们可以把它表示为\((c-a+1)\times bas^{0} + (b-a+1)\times bas^{1} +(a-a+1)\times bas^{2}\)
这里有几个需要注意的地方
首先进制的选择要大于字符的种类数,否则会有很大的概率出现冲突
还有就是我们在把字符转成整形的时候,可以直接使用它的\(ASCII\)码值,也可以用它减去一个字符
但是在使用第二种方法的时候,减去一个字符后要加上一个\(1\),否则会出现错误
比如字符串\(aaa\)和\(aa\),如果我们将每一个字符减去\(a\)后不把它加上\(1\)的话
最后两个字符串的哈希值都会变成\(0\),也就是说会把这两个字符串判成相等,会出现错误的结果
由于字符串的长度可能很大,因此如果我们一直把它的哈希值累加的话,很有可能会溢出
因此,我们要对某个字符串的哈希值取模,方法有两种
一种是选取一个较大的质数
比如\(19260817\)、\(19660813\)、\(1222827239\)、\(212370440130137957\)
另一种是使用\(unsigned long long\)使其自然溢出
其实后一种方法就相当于对\(2^{64}-1\)取模
还有一种操作是取出字符串中某一段字符\([l,r]\)的\(hash\)值
这时我们要用到一个公式\(ha[r]-ha[l-l]*pw[r-l+1]\)
其中\(ha[i]\)为该字符串前\(i\)位的\(hash\)值,\(pw[i]\)为进制\(bas\)的\(i\)次方

代码实现

我们拿洛谷P3370来举例子
这里我用的是自然溢出

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const int maxn=1e5+5;
ll f[maxn];
ll bas=233,cnt=0;
ll get_hash(char s[]){
    ll ans=0,len=strlen(s);
    for(ll i=0;i<len;i++){
        ans=ans*bas+s[i];
    }
    return ans;
}
char s[maxn];
int main(){
    int n;
    scanf("%d",&n);
    while(n--){
        scanf("%s",s);
        f[++cnt]=get_hash(s);
    }
    sort(f+1,f+1+cnt);
    int now=1;
    for(ll i=2;i<=cnt;i++){
        if(f[i]!=f[i-1]) now++;
    }
    printf("%d\n",now);
}

二、KMP字符串匹配

定义

\(KMP\)算法是一种改进的字符串匹配算法,由\(D.E.Knuth,J.H.Morris\)和\(V.R.Pratt\)提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称\(KMP\)算法)。\(KMP\)算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个\(next()\)函数实现,函数本身包含了模式串的局部匹配信息。\(KMP\)算法的时间复杂度\(O(m+n)\)
通俗的来说就是在需要匹配的那个串上给每个位置一个失配指针\(fail[j]\),表示在当前位置j失配的时候需要返回到\(fail[j]\)位置继续匹配,而这就是KMP算法优秀复杂度的核心。

实现

我们设\(fail[i]\)为第\(1\)-第\(i\)位中前缀与后缀相同的部分最长是多长。
这样,即可以理解为,若第\(i\)位失配了,则至少要往前跳多少步,才可能重新匹配得上。

标签:匹配,bas,ll,笔记,学习,哈希,KMP,字符串
来源: https://www.cnblogs.com/liuchanglc/p/13362938.html