题意
给定一个长度为 n 的字符串和数组 p,字符串的每次变换都将以数组 p 中的数字作为基准,问在经过最少几次(不可以为 0 0 0 )变换后,该字符串能够变换为与初始字符串相同。
对样例 3 手模后,可以得到如上表格(眼瞎了,可能有错,凑合看看规律 ),发现其中是以若干个环进行滚动的,如下标
(
1
,
8
,
3
)
,
(
2
,
6
)
,
(
4
,
7
,
9
,
10
)
,
(
5
)
(1,8,3),(2,6),(4,7,9,10),(5)
(1,8,3),(2,6),(4,7,9,10),(5) ,在最坏情况下求出每个环的长度的最小公倍数即为操作次数。
然而环中可能存在相同字符的情况,使得操作次数可以在该环的长度以内即可完成一次循环,特判该情况即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=210;
int p[N],vis[N];
int lcm(int a,int b){
return a/__gcd(a,b)*b;
}
void solve(){
int n;
string s;
cin>>n>>s;
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++)
cin>>p[i],p[i]--;
int ans=1;
for(int i=0;i<n;i++){
if(vis[i])
continue;
int j=i;
string x="";
while(!vis[j]){//当前环
x+=s[j];
vis[j]=1;
j=p[j];
}
int len=x.size(),minn=len;
for(int i=1;i*i<=len;i++){
if(len%i==0){
bool flag=1;
for(int j=0;j<i&&flag;j++)
for(int k=j;k<len&&flag;k+=i)
flag&=(x[j]==x[k]);
if(flag)
minn=min(minn,i);
//cout<<"flag="<<flag<<" minn="<<minn<<endl;
if(i*i<len){
bool ok=1;
for(int j=0;j<len/i&&ok;j++)
for(int k=j;k<len&&ok;k+=len/i)
ok&=(x[j]==x[k]);
if(ok)
minn=min(minn,len/i);
//cout<<"ok=="<<ok<<" minn="<<minn<<endl;
}
}
}
ans=lcm(ans,minn);
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr);
int T;
cin>>T;
while(T--)
solve();
return 0;
}