链接:
csoj | Cyclically Isomorphic (scnu.edu.cn)
知识储备:
一、字符串最小表示的含义:
字符串的最小表示即将一个字符串S转换为它与它的循环同构中字典序最小的一个。
二、求字符串最小表示的步骤
1.设置两个指针i,j和匹配长度k,初始化i=0,j=1,k=0。
2.比较S[i+k]和S[j+k],这时会出现三种情况:
(1)S[i+k]=S[j+k],则令k++;
(2)S[i+k]>S[j+k], 最小表示肯定不在S[i…i+k]范围内,于是更新,使i=i+k+1。
(3)S[i+k]<S[j+k],最小表示肯定不在S[j…j+k]范围内,于是更新,使j=j+k+1。
注意:更新i、j后可能出现i=j的情况,比较同个起点是无意义的, 故令i++或j++;
且由于起点更新了,两个串要重新开始比较,所以使k=0。
3.循环直到k>=n或i>=n或j>=n,随后返回min{i,j}即为最小表示的开始位置。
三、求字符串最小表示代码模板:
int I = 0, J = 1, k = 0;
while(I < m && J < m && k < m)
{
if (s[(I + k) % m] == s[(J + k) % m]) k++;
else
{
s[(I + k) % m] > s[(J + k) % m] ? I += k+1 : J += k+1;
if (I == J) I++;
k = 0;
}
}
int pos = min(I, J); //最小字符串的起始位置
思路:
找出字符串最小表示,然后利用字符串哈希判等
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
typedef unsigned long long ULL;
ULL a[N];
char s[N];
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%s", s);;
int I = 0, J = 1, k = 0;
while(I < m && J < m && k < m)
{
if (s[(I + k) % m] == s[(J + k) % m]) k++;
else
{
s[(I + k) % m] > s[(J + k) % m] ? I += k+1 : J += k+1;
if (I == J) I++;
k = 0;
}
}
int pos = min(I, J); //最小字符串的起始位置
a[i] = 0;
for (int j = 0; j < m; j++)
a[i] = a[i] * 131 + s[(pos + j) % m];
}
int Q;
scanf("%d", &Q);
while(Q--)
{
int x, y;
scanf("%d%d", &x, &y);
if (a[x] == a[y]) puts("Yes");
else puts("No");
}
}
return 0;
}