扶苏和串 题解
I_am_AKed_by_NOI · · 题解
update on 7-20 修改了一些错别字和渲染失败的问题,同时贴上了代码并详细讲解了些细节。
暴力解法
观察题目,我们可以想到一个暴力。我们暴力去枚举翻转区间的左端点
但是复杂度并不乐观:枚举
由于这是入门赛,为了方便新手,这里多讲一下细节(大佬请自动跳过):
- 如何比对两个字符串的字典序大小
我们令两个字符串为
一部分人会说,将两个字符串遍历一遍,然后判断两个字符串同样位置上的字符的大小。
其实并不用那么麻烦,其实只需要一句话。
if(s1<s2)
如果成立,那么
- 如何对一段字符进行翻转
如果我们要翻转一个长度为
这里提供另外一种思路,假设翻转字符串
luogu
ugoul
发现,原字符符第
正解
其实正解与暴力之间只有一个优化,那就是:翻转的左端点其实是固定的!!!
有一个结论:翻转的左端点必定是字符串
通过次结论,我们就不需要暴力去枚举
大部分题解都没有证明这个结论,在这里我证明一下这个结论:
我们记字符串
发现,
现在分类讨论:
-
如果以
x 作为左端点进行翻转,右端点一定是1 ,因为x 后面没有0 的出现,所以旋转区间内的所有数都为1 ,翻转后与原字符串相同。 -
如果以
x 右侧一点(设为a_3 )作为左端点进行翻转,类似地,翻转后与原字符串相同。 -
如果以
x 右侧一点(设为a_4 )作为左端点进行翻转,如果右端点(设为a_5 ) 在x 左侧,显然的S_{a_4},S_{a_4+1},...S_{a_5} 之间所有的数都为0 ,翻转后与原字符串相同。如果右端点(设为a_5 ) 在x 右侧,那么S_{a_5} 一定为1 ,翻转后S_{a_4} 就会由0 变成1 ,字典序比原字符串大,不是最优。
通过以上所有的证明,可以得出:翻转的左端点是字符串
代码
注释对上面的过程进行讲解:
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
string s1,s2,ans; //ans 是最后输出的答案
int l,r; //l 是字符串中第一次出现 1 的位置,即翻转的左端点
int main()
{
cin>>s1;
s2=s1; //进行备份
ans=s1;
for(int i=0;i<s1.length();i++) //遍历整个字符串,寻找第一次出现 1 的位置
{
if(s1[i]=='1')
{
l=i; //找到了就返回
break;
}
}
for(r=l+1;r<s1.length();r++) //枚举旋转的右端点
{
for(int j=l,k=r;j<=r,k>=l;j++,k--)
{
s2[j]=s1[k]; //将字符串的 [l,r] 进行翻转
}
if(s2<ans) //如果旋转之后的字符串字典序更小
{
ans=s2; //就将答案更新为字典序更小的字符串
}
}
cout<<ans;
return 0; //漂亮的结尾!
}
完结撒花!!
程序和思路可以带走,请把赞留下,谢谢辣!(可爱)