2021江苏省赛 H-Reverse the String (哈希,二分)

来源

题目

There is a string of lowercase letters, and you want to minimize its lexicographical order. What you can do is reverse an interval or do nothing.

For example, for the string abcdefg, if we reverse the interval abcdefg, it will become abfedcg.

A string aa is lexicographically smaller than a string bb if and only if one of the following holds:

  • aa is a prefix of bb, but a≠ba≠b.
  • In the first position where aa and bb differ, the string aa has a letter that appears earlier in the alphabet than the corresponding letter in bb

Input

The first line contains an integer TT (1≤T≤201≤T≤20), denoting the number of test cases.

For the following TT lines, each line contains a string ss of lowercase letters (1≤|s|≤1051≤|s|≤105, where |s||s| is the length of ss).

It is guaranteed that ∑|s|≤1.5×106∑|s|≤1.5×106 over all test cases.

Output

For each test case, output the string with the minimal lexicographical order in a single line.

题意

给定一个字符串,可以最多翻转任意区间一次。问能得到的字典序最小的字符串是什么。字符串的长度是1e5范围。

思路

       首先数据范围摆在这,枚举两个端点是不可接受的,最多只能枚举一个端点,所以应该是有一端可以固定。

        因为要做到字典序最小,所以我们先将字符串每个字符进行排序,从头往后看,如果当前的位置和排序后的字符串是相同的,说明这个位置已经是最优的情况,不需要翻转,如果出现第一个不是最优,那么后面必然有个字符能翻转上来使得至少在这个位置上是最优的,所以就可以贪心的选好翻转区间的左端点了。而且翻转区间右端点位置上的字符也可以确定下来。

       接着就是判断右边所有符合条件的端点哪个是最优的,这个必须得逐个判断,至少是O(n)的复杂度,还剩个log用于比较两种翻转哪个更优。可以二分判断两个字符串的公共前缀,具体的check函数就是检查前mid个字符的哈希值是否相同。然后找到第一位不同的进行比较。

      对k个可能的右端点,进行k-1次比较,然后输出答案即可。

#include<bits/stdc++.h>

using namespace std;
const int N = 1e5+100;

typedef unsigned long long ull;

ull bas=13313;
char s[N],ss[N];
ull h1[N],h2[N];
ull a[N];
ull st;
vector<int>v;

ull query1(ull l,ull r){
	return h1[r]-h1[l-1]*a[r-l+1];
}
ull query2(ull l,ull r){
	return h2[l]-h2[r+1]*a[r-l+1];
}
bool check(ull x,ull ed,ull ed2){
	ull hash1;
	if(st+x-1<=ed){
		hash1=query2(ed-x+1,ed);
	}else{
		hash1=query2(st,ed)*a[st+x-ed-1]+query1(ed+1,st+x-1);
	}
	ull hash2=query2(ed2-x+1,ed2);
	if(hash1==hash2)return true;
	else return false;
}
void solve(){
    cin>>s+1;
    ull n=strlen(s+1);
    a[0]=1;
    for(int i=1;i<=n;i++){
    	h1[i]=h1[i-1]*bas+s[i]-'a'+1;
    	a[i]=a[i-1]*bas;
	}
	for(int i=n;i>=1;i--){
		h2[i]=h2[i+1]*bas+s[i]-'a'+1;
	}
	for(int i=1;i<=n;i++){
		ss[i]=s[i];
	}
	ss[n+1]='\0';
	sort(ss+1,ss+n+1);
	st=0;
	char c;
	for(int i=1;i<=n;i++){
		if(ss[i]!=s[i]){
			st=i;
			c=ss[i];
			break;
		}
	}
	if(st==0){
		cout<<s+1<<'\n';
		return;
	}
	v.clear();
	for(int i=st;i<=n;i++){
		if(s[i]==c)v.push_back(i);
	}
	ull ed=v[0];
	for(int i=1;i<v.size();i++){ 
		ull ed2=v[i];
		ull hash1=query2(st,ed)*a[ed2-ed]+query1(ed+1,ed2);
		ull hash2=query2(st,ed2);
		if(hash1==hash2)continue;
		ull l=0,r=ed2-st+1;
		while(l<r){
			ull mid=(l+r)/2;
			if(check(mid,ed,ed2))l=mid+1;
			else r=mid;
		}
		if(st+l-1<=ed){
			if(s[ed-l+1]>s[ed2-l+1])ed=ed2;
		}else{
			if(s[st+l-1]>s[ed2-l+1])ed=ed2;
		}
	}
	for(int i=1;i<st;i++){
		cout<<s[i];
	}
	for(int i=ed;i>=st;i--){
		cout<<s[i];
	}
	for(int i=ed+1;i<=n;i++){
		cout<<s[i];
	}
	cout<<'\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin>>t;
    while (t--)solve();
}

  • 26
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心刍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值