【字符串哈希】2020牛客国庆集训派对day1 Problem A: ABB

Problem: ABB

传送门
【字符串哈希】2020牛客国庆集训派对day1 Problem A: ABB_第1张图片
【字符串哈希】2020牛客国庆集训派对day1 Problem A: ABB_第2张图片
【字符串哈希】2020牛客国庆集训派对day1 Problem A: ABB_第3张图片
题目大意:
给出一个长度为 n n n 的字符串,问至少在原串的末尾添加多少个字符可以使这个串变为回文串。
题意转换:
在原串的基础之上,求包含末尾字符的最长的回文子串,若该最长回文子串的长度为 l e n len len,则答案 a n s = n − l e n ans = n - len ans=nlen;
解题思路:
算法:字符串哈希(双向哈希)
看一波范围, n ∈ 4 e 5 n∈4e5 n4e5。求最长回文子串,普通的做法是两层for循环来求,显然在该数据范围下不能使用该做法。那就;有一个比较优秀的算法是马拉车算法 ( M a n a c h e r ‘ s   A l g o r i t h m Manacher‘s\ Algorithm Manachers Algorithm),可以在 O ( n ) O(n) O(n) 的时间复杂度下求出最长回文子串。确实比较优秀哈,但是,该算法貌似也仅仅能求最长回文子串,相关的扩展问题几乎没有,可以算的上冷门算法,在此也就不对马拉车算法做相关解释了(其实是博主还没有学会,逃。。。)
接下来且看本文核心内容
关于该题,使用字符串哈希的算法来解,简直太方便了。由于该最长回文子串必须包含最后一个字符,也就是说,它的位置已经是确定的了。这样的话,利用字符串哈希同样可以在 O ( n ) O(n) O(n) 的时间复杂度下求出最长回文子串。
【字符串哈希】2020牛客国庆集训派对day1 Problem A: ABB_第4张图片
字符串哈希,利用p进制的思想来表示字符串。
此题比较巧妙的是使用了双向哈希。这样可以在 O ( 1 ) O(1) O(1) 的时间复杂度判断某个串是否是回文子串。
我个人感觉不必对子串的长度奇偶性进行考虑,判断左半边和右半边的子串是否相等,这里的一半,我们取 ⌈ n 2 ⌉ \lceil{\frac{n}{2}}\rceil 2n, n n n 是要判断是否是回文串的那个字符串长度。
字符串哈希不会怎么办? 这就去学习

上代码:

#include
using namespace std;
typedef unsigned long long ull;
const int P = 131;
const int N = 400010;
ull hl[N],p[N],hr[N];
ull get(ull h[],int l,int r)
{
     
    return h[r] - h[l - 1] * p[r - l + 1];
}
char str[N];
int main()
{
     
    int n; scanf("%d",&n);
    scanf("%s",str + 1);
    p[0] = 1;
    for(int i = 1,j = n;i <= n;i++,j--)
    {
     
        hl[i] = hl[i - 1] * P + str[i];
        hr[i] = hr[i - 1] * P + str[j];
        p[i] = p[i - 1] * P;
    }
    for(int i = 1;i <= n;i++)
    {
     
        int len = n - i + 1;
        len /= 2;
        if(get(hl,i,i + len - 1) == get(hr,1,len))
        {
     
            cout << i - 1 << endl;
            break;
        }
    }
    return 0;
}
/*
a#b#b#b#b
123456789
*/

另外还有一道题,比这个题稍微复杂一点点。

Problem :AcWing 139.回文子串的最大长度

传送门
【字符串哈希】2020牛客国庆集训派对day1 Problem A: ABB_第5张图片
解题思路:
算法:字符串哈希 + 二分(双向哈希)
这个题的数据范围是 1 e 6 1e6 1e6, 而且最长回文子串的位置不固定,要找到最长回文子串的话就需要加上二分来判断了,二分最长回文子串的半径,很明显可以知道二分半径是具有单调性的,半径小的不是回文串的话,半径更大的时候必定也是非回文的。
为了便于计算回文串的长度,我们将原字符串做以下处理,在每两个字符之间添加一个 ‘#’,这样的话,处理所有的字符串的时候,我们面临的全部都是长度为奇数的字符串(偶数长度的子串必定不是回文串例如:a#a#或#a#a)。若出现回文子串,只能出现两种形式:
1、#a#…#a# 去 掉 特 殊 字 符 后 , 回 文 串 的 长 度 与 半 径 相 等 去掉特殊字符后,回文串的长度与半径相等
2、a#…#a     去 掉 特 殊 字 符 后 , 回 文 串 的 长 度 等 于 半 径 加 1 \ \ \ 去掉特殊字符后,回文串的长度等于半径加1    1
对于所有符合回文条件的串的长度取最大值即为答案。

上代码:

#include 
#include 
#include 
#include 
using namespace std;
typedef unsigned long long ull;
const int N = 2000010;
char str[N],s[N];
const int P = 131;
ull p[N],hl[N],hr[N];
ull get_hash(ull h[],int l,int r)
{
     
    return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
     
    int k = 1;
    while(scanf("%s", str + 1), strcmp(str + 1, "END"))
    {
     
        int n = strlen(str + 1);
        int len = 0;
        for(int i = 1;i <= n;i++)
        {
     
            s[++len] = str[i];
            if(i < n) s[++len] = '#';
            else s[len + 1] = '\0';
        }
        p[0] = 1;
        for(int i = 1,j = len;i <= len && j >= 1;i++,j--)
        {
     
            p[i] = p[i - 1] * P;
            hl[i] = hl[i - 1] * P + s[i];
            hr[i] = hr[i - 1] * P + s[j];
        }
        int ans = 0;
        for(int i = 1;i <= len;i++)
        {
     
            int l = 0,r = min(len - i,i - 1);
            while(l < r)
            {
     
                int mid = (l + r + 1) >> 1;
                if(get_hash(hl,i - mid,i - 1) != get_hash(hr,len - i - mid + 1,len - i)) r = mid - 1;
                else l = mid;
            }
            if(s[i - l] == '#') ans = max(ans,l);
            else ans = max(ans,l + 1);
        }
        printf("Case %d: %d\n",k++,ans);
    }
    return 0;
}
/*
#a#b#a#
a#a#a#a
1234567
*/

再送一道比较基础的字符串哈希题
单向哈希即可。

AcWing.841字符串哈希

传送门
【字符串哈希】2020牛客国庆集训派对day1 Problem A: ABB_第6张图片

模板题,比较基础,我直接放上代码了。(不懂没关系,会用就很棒)
上代码:

#include 
#include 
#include 
#include 
using namespace std;
const int N = 100010;
int P = 131;
typedef unsigned long long ull;
ull p[N],h[N];
char str[N];
ull get_hash(int l,int r)
{
     
    return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
     
    int n,m; cin >> n >> m;
    cin >> str + 1;
    p[0] = 1;
    for(int i = 1;i <= n;i++)
    {
     
        p[i] = p[i - 1] * P;
        h[i] = h[i - 1] * P + str[i];
    }
    for(int i = 1;i <= n;i++)
    {
     
        cout<<get_hash(i,i)<<" ";
    }
    cout<<endl;
    while(m--)
    {
     
        int l1,r1,l2,r2;
        cin >> l1 >> r1 >> l2 >> r2;
        if(get_hash(l1,r1) == get_hash(l2,r2)) puts("Yes");
        else puts("No");
    }
    return 0;
}

特别提醒:危!!!p[0]一定要初始化为1呀。(忘记好几次,得调半天,呜呜呜)

你可能感兴趣的:(数据结构,算法,字符串)