“统信杯” 第十七届黑龙江省大学生程序设计竞赛 Let‘s Swap

Let’s Swap(思维+hash)4星

题意:

已知一个长度为n的字符串 S S S,可以对字符串进行一种操作:

  1. 将字符串 S S S从下标 i ( 1 ≤ i ≤ n ) i(1\leq i\leq n) i(1in)的位置将串分为两个串 A 、 B A、B AB
  2. 将字符串 S S S变为 B A BA BA
  3. 反转字符串 S S S

给你两个数 a 、 b a、b ab代表可以选取的下标的位置,允许操作任意次(或不操作)

S S S是否可以通过操作变成目标串 C C C

多组输入

1 ≤ T , s u m ∣ S ∣ ≤ 5 ∗ 5 10 1\leq T,sum|S|\leq 5*5^{10} 1T,sumS5510

题解:

由于只能选取两个位置进行操作,考虑操作的方案:(假设 a > b a>b a>b

  • 只执行一次 a a a或只执行一次 b b b——按照题目所述操作即可

  • 执行多次操作

    • 显然连续选取同一位置进行操作是无效的

    • 考虑先选 a a a再选 b b b,用打表可知字符串操作变为:

      选择位置 j = n − ( a − b ) j=n-(a-b) j=n(ab)将串 S S S分为串 A 、 B A、B AB,将串变为 B A BA BA

      若多次执行当前先选 a a a再选 b b b的操作,则字符串始终按照循环节为 j j j进行操作

    • 考虑先选 b b b再选 a a a,同上只不过 j = a − b j=a-b j=ab

    • 考虑 a 、 b a 、 b a 、 b a 、 b a . . . a、ba、ba、ba、ba... ababababa...的情况,只需要求出按 a a a执行一次操作的串 S 1 S1 S1,然后就变成了上面的情况

    • b 、 a b 、 a b 、 a b 、 a b . . . b、ab、ab、ab、ab... babababab...同理

如何判断三个串 S 、 S 1 、 S 2 S、S1、S2 SS1S2是否可以通过上述操作来变成 C C C

由上述推断可得,只要我们枚举按照循环节 j j j操作之后的字符串,若能得到串 C C C,说明yes,否则是no

匹配字符串可以用hash来判断,因为要判断循环节,所以可以将字符串都变成二倍

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define mod1 1000000007
#define mod2 1000000009
#define endl '\n'
//#define int long long
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int N=5e5+10,P=131;
int n,m,a,b;
string s,s1,s2;
string c;
ull h[2*N],h1[2*N],h2[2*N],res;
ull p1[2*N],p2[2*N];
ull work(ull h[],int l,int r){
	return h[r]-h[l-1]*p1[r-l+1];
} 
bool get(int l,int r){
	if(work(h,l,r)==res){
		//cout<<l<<' '<<r<<" 1"<<endl;
		return true;
	} 
	if(work(h1,l,r)==res){
		//cout<<l<<' '<<r<<" 2"<<endl;
		return true;
	}
	if(work(h2,l,r)==res){
		//cout<<l<<' '<<r<<" 3"<<endl;
		return true;
	}
	return false;
}
void solve(){
	cin>>s>>c>>a>>b;
	n=s.size();
    //构造串S1、S2
	s1=s.substr(a)+s.substr(0,a);
	reverse(s1.begin(),s1.end());
	s2=s.substr(b)+s.substr(0,b);
	reverse(s2.begin(),s2.end());
    
	//求出每个串的hash值
	s=s+s,s="#"+s;
	s1=s1+s1,s1="#"+s1;
	s2=s2+s2,s2="#"+s2;
	c="#"+c;
	h[0]=h1[0]=h2[0]=0;
	p1[0]=1,p2[0]=1;
	res=0;
	for(int i=1;i<=2*n;i++){
		p1[i]=p1[i-1]*P;
		h[i]=h[i-1]*P+s[i]-'a'+1;
		h1[i]=h1[i-1]*P+s1[i]-'a'+1;
		h2[i]=h2[i-1]*P+s2[i]-'a'+1; 
		if(i<=n) res=res*P+c[i]-'a'+1;
	}
    
    //按照循环节来判断是否可以变成C
	if(a<b) swap(a,b);
	for(int i=1,j=1;j<=n;j++){
		if(get(i,i+n-1)){
			cout<<"yes"<<endl;
			return ;
		}
		i=i+n-a+b;
		if(i>=n+1) i=i%(n+1)+1;
	}
	for(int i=1,j=1;j<=n;j++){
		if(get(i,i+n-1)){
			cout<<"yes"<<endl;
			return ;
		}
		i=i+a-b;
		if(i>=n+1) i=i%(n+1)+1;
	}
	cout<<"no"<<endl;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T=1;
	cin>>T;
	while(T--){
		solve();
	} 
	return 0;
}
/*
1
abcde
abcde
1 2
*/
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值