样例如下
输入:
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)降到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;
}