KMP算法 与 字符串哈希

基础 kmp

KMP算法用于在线性时间复杂度内寻找字符串中是否含有某个子串的情况

本质上是通过移动待查询子串的相对位置来寻找是否有该子串, 移动的方式则通过Next数组来记录

Next数组中记录的是最大相同前后缀的长度

视频: 最浅显易懂的 KMP 算法讲解_哔哩哔哩_bilibili

KMP寻找所有匹配区间

int f[N + 10];

//预处理
void getfill(string s) {
    memset(f, 0, sizeof(f));
    for (int i = 1; i < s.length(); i++){
        int j = f[i];
        while (j && s[i] != s[j])
            j = f[j];
        f[i + 1] = (s[i] == s[j]) ? j + 1 : 0;
    }
}

//记录所有匹配区间 [指在长串中的下标,下标从0开始]
struct Interval{
    int l, r;
}q[N];

//记录匹配次数
int t;

//找到并记录所有的匹配区间, a长 s短
void findinterval(string a, string s){
    getfill(s);
    int j = 0; 
    t = 0;
    for (int i = 0; i < a.length(); ++i){
        while (j && a[i] != s[j]) 
            j = f[j];
        if (a[i] == s[j]) 
            j++;
        if (j == s.length()){
            t++;
            q[t].l = i - s.length() + 1;
            q[t].r = i;
        }
    }
}

拓展 kmp (待补充)

字符串哈希

通过一个哈希值来表示整个字符串的方法

求哈希值需要两个素数 base 和 mod , 一般 base < mod , 尽量取大

一般 base == 131 , mod == 19260817 或 1e9+7 即可

对一段标记为 s == x1x2x3...xn 的字符串, 有如下公式

单哈希公式

hash[i]=(hash[i-1]*base + F(s[i]))\%mod

其中 F(s[i]) 表示字符串中第 i 个字符所映射到的数值, 例如 'a' 映射到 1 , 'z' 映射到 26

求某段子串的哈希值

res = ((hash[r] - hash[l-1]*base^{r-l+1})\%mod+mod)\%mod

base 幂的地方一般先预处理一遍

实例代码如下

ll base = 131;
ll mod = 1e11+7;
ll has[N];
ll p[N];

void init()
{
	p[0] = 1;
	for (ll i = 1; i < N; i++)
	{
		p[i] = p[i - 1] * base;
	}
}

ll hashs(string s)
{
	has[0] = 0;
	for (ll i = 0; i < s.length(); i++)		//配合string的下标表示
	{
		has[i+1] = (has[i] * base + (s[i] - 'a' + 1)) % mod;
	}
	return has[s.length()];
}

ll gethas(ll l, ll r)
{
	return ((has[r] - has[l - 1] * p[r - l + 1]) % mod + mod) % mod;
}

二维哈希

与双哈希并非一个意思, 双哈希用于进一步降低哈希冲突的概率, 而二维哈希是用于解决矩阵性质的问题

原理与一维哈希类似, 通过先对每一行横向求哈希值, 在此哈希表的基础上再对每一列纵向求哈希值, 由此得到一个哈希阵列, 因此二维哈希需要两个base, 也同样需要两次预处理

二维哈希公式

has[i][j] = has[i][j - 1] * base1 + F(M[i][j])

has[i][j] = has[i-1][j] * base2 + F(M[i][j])

求子矩阵 (a, b) * (x, y) 的哈希值                (a <= x, b <= y)

res = hash[x][y] - hash[x][b-1]*base_{1}^{y-b+1} - hash[a-1][y]*base_{2}{}^{x-a+1} + hash[a-1][b-1]*base_{1}^{y-b+1}*base_{2}{}^{x-a+1}

实例代码如下

ll hashs(ll x, ll y)
{
    has[0][0] = 0;
    for(ll i = 1; i<N;i++)
    {
        has[0][i] = 0;
        has[i][0] = 0;
    }
    for (ll i = 1; i <= x; i++)
    {
        for (ll j = 1; j <= y; j++)
        {
            has[i][j] = (has[i][j - 1] * base1 + (M[i][j] - 'a' + 1));
        }
    }
    for (ll j = 1; j <= y; j++)
    {
        for (ll i = 1; i <= x; i++)
        {
            has[i][j] = (has[i - 1][j] * base2 + has[i][j]);
        }
    }
    return has[x][y];
}


ll gethas(ll a, ll b, ll x, ll y)
{
    return has[x][y] - has[x][b - 1] * p1[y - b + 1] - has[a - 1][y] * p2[x - a + 1] + has[a - 1][b - 1] * p1[y - b + 1] * p2[x - a + 1];
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值