Codeforces Round #797 (Div. 3) F. Shifting String题解

题意是给一个字符串s和一个与之相同长度的数组排列a,字符串s按照数组给定的位置进行变化,新字符串的第i个字符是上一个字符串的第a[i]个字符

比如 例子

5

ababa

2 1 4 5 3

变化6次

babaa

abaab

baaba

abbaa

baaab

ababa

变回原来的字符串,问需要最少几次变化为原来的字符串

第一个点,观察发现,字符串变化时一些位置的字符变化时循环的,比如上例的ababa的前两个字符ab,因为a[1]=2,a[2]=1,第一个字符到第二个字符,第二个字符到第一个字符,如此一直循环。因此可以建图,从结点 i 连一条边到结点 a[i] ,可以得出一个结论,所有这种图上的环都可以到变回到原来的样子,这是第一个点,应该不难发现

第二个点,这种环需要几次变化才能回到原来状态呢,开始我想当然以为如果环上字符都相等是一次,否则为环上结点的个数,直接wa了,后来想了一下应该是字符串的子字符串循环的最小长度

比如abcabc是3,因为变化3次为bcabca,cabcab,abcabc

第三个点,知道了每个环上变换的次数,那么总次数是多少呢,我又想当然的以为是全部乘起来,然后又wa了,其实想了一下应该是每个环的变换次数的最小公倍数(我好笨......),因为最小公倍数能保证每个环的循环的次数的倍数,让其回到原来的状态,所以写个lcm就可以过了

一个1700的比较综合的题目,记录一下,wa了两发才过,刚放暑假好久没写代码手都生了,看来还得再仔细一点

以下为AC代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[210];
int g[210];
bool used[210];
ll gcd(ll a,ll b)
{
	return b?gcd(b,a%b):a;
}


ll lcm(ll a,ll b)
{
	return a*b/gcd(a,b);
}//求最小公倍数

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
    	int n;
    	scanf("%d%s",&n,s+1);
    	for(int i=1;i<=n;i++)
    	{
    		used[i]=false;
    		int k;
    		cin>>k;
    		g[i]=k;
		}
		ll ans=1;
		for(int i=1;i<=n;i++)
		{
			if(!used[i])
			{
			int nodenum=0;
			
				string tmp="";
				int loc=i;
				while(!used[loc])//把环上字符连成串
				{
					used[loc]=true;
					tmp+=s[loc];
					loc=g[loc];
                }
                int sl=tmp.length();
				for(int i=1;i<=sl;i++)//求变化最小次数
				{
					if(sl%i==0)
					{
						bool flag=true;
						for(int j=0;j<i;j++)
						{
							char pre=tmp[j];
							for(int k=j;k<sl;k+=i)
							{
								if(pre!=tmp[k])
								{
									flag=false;
									break;
								}
							}
							if(!flag) break;
						}
						if(flag) 
						{
							nodenum=i;
							break;
						}
					}
				}
			ans=lcm(ans,(ll)nodenum);
			}
	
		}
		cout<<ans<<"\n";
	}
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_30798083/article/details/125317174