2023年杭电多校第一场-----E (5). Cyclically Isomorphic (含字符串哈希知识点讲解)

样例如下

输入:

2
2 2
ab 
ba
1
1 2
4 3
aab
baa
bba
bab
6
1 2
1 3
1 4
2 3
2 4
3 4

 输出:

Yes
Yes
No
No
No
No
Yes

 题意概述:给定n个字符串,这个字符串是可以循环右移的,指 abcd 可以变成 bcda 或者 cdab ......然后有Q次询问,每次询问问你第x个字符串和第y个字符串是否可以通过循环右移变成相等的字符串。

前置知识:字符串哈希

思路分析:可以通过字符串哈希预处理使时间复杂度从暴力的O(n^2)变成O(n), 每次查询的时间复杂度从O(n)降到O(1), 这样就不用担心超时问题了。这里每个字符串只需算他最小的哈希值就行,当然最大的哈希值也可以哦。 然后这个最小的哈希值就是这个字符串的标识的数子,我们可以用这个数字来确定这个字符串。然后查询的话因为给了你两个数x, y 直接判断这两个位置上的哈希值是否相同就行了。

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N = 1e5 + 10;
const ull prime = 131;
ull h[N], p[N];
char s[N];
ull s_h[N];
void init()//预处理
{
    p[0] = 1;
    for(int i =  1; i <= 1e5; i ++)//算prime的几次方
    {
        p[i] = p[i - 1] *prime;
    }
}
ull get(int l, int r)//算l - r 的字符串的哈希值,算法类似前缀和。
{
    return h[r] - h[l - 1] * p[r - l + 1];
}
void solve()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i ++)
    {
        cin >> s + 1;
        for(int j = 1; j <= m; j ++)//计算这个字符串的哈希值
        {
            h[j] = h[j - 1] * prime + s[j];
        }
        ull mmin = h[m];//没有移动的哈希值
        for(int j = 1; j <= m; j ++)//这里计算算把前j位移到移到末尾的哈希值
        {

            ull qian = get(1, j);//取前j位
            ull hou  = get(j + 1, m);//取j位以后的数
            ull temp = hou * p[j] + qian;//后面j位以后的左移j位到首位,然后加上前j位的哈希值
            mmin = min(mmin, temp);//取最小的哈希值
        }
        s_h[i] = mmin;
    }

    int q;
    cin >> q;
    while(q --)
    {
        int x, y;
        cin >> x >> y;
        if(s_h[x] == s_h[y])//判断对应位置的哈希值是否相同
        {
            cout << "Yes\n";
        }
        else cout << "No\n";
    }
}
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t;
    init();
    cin >> t;
    while( t --)
    {
        solve();
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值