用hash搞manacher和kmp

搞manacher
最长回文子串!我知道!马拉车!
可是如果你马拉车写挂了呢?或者像我一样不会马拉车
这时候就得靠hash来水分了
我们知道,回文子串是具有单调性的
如果字符串 Slr为回文子串,那么Sxy也一定是回文子串
单调性!我们是不是可以二分?
我们暂时只讨论长度为奇数的回文子串。(事实上,长度为偶数的回文子串与奇数的只是处理上的一些细节不同,仅此而已)
考虑枚举回文子串的中点,并二分回文子串的长度(不过一般来说,二分回文子串的长度的1/2可能会更好写一点),那么我们使用上文提到的 [公式] 查询子串 [公式] 值的方法,就可以 [公式] 判断二分得到的这个子串是不是回文子串了。
对于长度为偶数的回文子串,枚举中点左边/右边的字符即可

字符串的很多特性是具有单调性的,二分求解是一个常见的思路,配合哈希进行判断操作一般可以做到在nlogn效率内完成问题

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
#define N 10100
#define base 13131

char s[N];
ull h1[N], p[N], h2[N], ans = 0;
int n;

ull gh1(int l, int r) { return h1[r] - h1[l - 1] * p[r - l + 1]; }
ull gh2(int l, int r) { return h2[l] - h2[r + 1] * p[r - l + 1]; }

ull query1(int x) { //奇 
    int l = 1, r = min(x, n - x);
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(gh1(x - mid, x + mid) == gh2(x - mid, x + mid)) l = mid + 1;
        else r = mid - 1; 
    }
    return r;
}

ull query2(int x) { //偶 
    int l = 1, r = min(x, n - x); 
    while(l <= r) {
        int mid = (l + r) >> 1;
        if(gh1(x - mid + 1, x + mid) == gh2(x - mid + 1, x + mid)) l = mid + 1;
        else r = mid - 1;
    }
    return r;
}

int main() {
    scanf("%s", s + 1); p[0] = 1;
    n = strlen(s + 1);
    for(int i = 1; i <= n; ++i) {
        h1[i] = h1[i - 1] * base + s[i];
        p[i] = p[i - 1] * base;
    }
    for(int i = n; i; i--) h2[i] = h2[i + 1] * base + s[i];
    for(int i = 1; i < n; ++i) {
        ans += query1(i) + query2(i);
    }
    printf("%llu\n", ans + n);
}

搞kmp
给出两个字符串 s1 和 s2,其中s2 为s1 的子串,求s2 在 s1 中出现多少次/出现的位置。
如果有认真看过该篇文章的第一子目的话,应该不难想到这题的hash做法。
具体做法是预处理出来两个串的hash值,因为求的是 s2在s1 中出现的次数,所以我们要匹配的长度被压缩到了 s1 的长度,所以我们只需要枚举 s2 在 s1中的起点,看看后面一段长度为len的区间的hash值和 s2的hash值一不一样就好。

时间复杂度是 n+m,和kmp算法一样!

#include <bits/stdc++.h>
using namespace std;

#define N 1000010
#define ull unsigned long long
#define base 233

ull h[N], p[N], ha;
char s1[N], s2[N];

int main() {
    scanf("%s%s", s1 + 1, s2 + 1);
    int n = strlen(s1 + 1), m = strlen(s2 + 1);
    for(int i = 1; i <= m; ++i) ha = ha * base + (ull)s2[i];
    p[0] = 1;
    for(int i = 1; i <= n; ++i) {
        h[i] = h[i - 1] * base + (ull)s1[i];
        p[i] = p[i - 1] * base;
    }
    int l = 1, r = m, ans = 0;
    while(r <= n) {
        if(h[r] - h[l - 1] * p[m] == ha) ++ans;
        ++l, ++r;
    }
    printf("%d\n", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值