2019浙江省赛 K Strings in the Pocket

链接 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=4110

题意:给你两个字符串s和t,问你有多少种方法,在仅翻转一次s的子串后使得两个串相同。

题解:分两种情况。
1.两个串不同
我们先找到两个串不同点的边界值l,r,也就是最左边的那个不同点和最右边的那个不同的点。
显然可得如果翻转后两个串相同的话,那最小的翻转区间就是并且必须是[l,r].
反证明如下:
如果我们翻转区间的左端点大于l,或者右端点小于r,那 l / r 肯定没有改变,也就是说翻转完以后两个串不可能相同。
而如果我们以 l 为左端点,以x(x>r)为右端点,那我们可知s[x]=t[x]。假如翻转后两个串相等,那就会有s[l]=t[x],s[x]=t[l] ,加上s[x]=t[x] ,得出s[l]=t[l],与我们s[l]!=t[l]的条件矛盾。r同理。
然后我们从l,r向内递推判断翻转后是否相等,也就是:

while(L<=R&&L>=0&&R<len1){
    if(s[L]!=t[R]||s[R]!=t[L]){
        f=0;
        break;
    }
    L++,R--;
}

如果我们得到[l,r]翻转之后能使得两个串相同,那我们接下来就可以尝试往外拓展,

ans=0;
while(s[l]==t[r]&&s[r]==t[l]&&l>=0&&r<len1){
    ans++;
    l--,r++;
}

这样就能得到答案。

2.两个串完全相同
该情况涉及到一个算法:Manacher
不会的可以先去看看这篇博客:https://blog.csdn.net/happyrocking/article/details/82622881
Manacher算法实现的就是求出以字符串每个点(包括两个点之间的边界)为中心的最大回文串长度。时间复杂度是O(n)。
如果两个串相同,那我们只要翻转s串中任意一个回文串,这两个串就会保持原状,而使用Manacher算法能让我们在线性时间内求出每个点为中心的最大回文串长度。
对于任意一个中心点,我们翻转半径小于该点最大回文串半径的字串,就相当于翻转了一个回文串,原串保持不变,因此在该点我们可以进行的操作数x即为该点最大回文串半径的值。

for(int i=1;i<len2;i++){//len2为Manacher处理过后的串长。
    ans+=p[i]/2;//p[i]为以该点为中心的最大回文串长+1,因此我们除以2就能得到半径。
}

完整代码:

#include<stdio.h>
#include<string.h>
const int N=4e6;
char str[N<<1];
int p[N];
int len2,len1;
char s[N],t[N];
int min(int a,int b){
    return a<b?a:b;
}
int max(int a,int b){
    return a>b?a:b;
}
void Manacher(){
    for(int i=0;i<len1*2+4;i++)p[i]=0;
    str[0]='$',str[1]='#';
    for(int i=0;i<len1;i++){
        str[i*2+2]=s[i];
        str[i*2+3]='#';
    }
    int mx=0,id=0;
    len2=len1*2+2;
    str[len2]='#';
    for(int i=1;i<len2;i++){
        p[i]=i<mx?min(p[2*id-i],mx-i):1;
        while(str[i+p[i]]==str[i-p[i]])++p[i];
        if(i+p[i]>mx)mx=i+p[i],id=i;
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%s%s",s,t);
        len1=strlen(s);
        int l=len1,r=-1,flag=1;
        for(int i=0;i<len1;i++){
            if(s[i]!=t[i]){
                l=min(l,i);
                r=max(r,i);
                flag=0;
            }
        }
        if(flag){
            Manacher();
            long long ans=0;
            for(int i=1;i<len2;i++){
                ans+=p[i]/2;
            }
            printf("%lld\n",ans);
        }
        else{
            int L=l,R=r;
            int f=1;
            while(L<=R&&L>=0&&R<len1){
                if(s[L]!=t[R]||s[R]!=t[L]){
                    f=0;break;
                }
                L++,R--;
            }
            int ans=0;
            if(f==0){
                ans=0;
            }
            else{
                ans=0;
                while(s[l]==t[r]&&s[r]==t[l]&&l>=0&&r<len1){
                    ans++;
                    l--,r++;
                }
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

全部评论

相关推荐

||&nbsp;先说下主播个人情况:211本,暑期实习之前有过一段中大厂的后端实习,暑期拿过腾讯的实习offer,综合考虑业务和语言最终去了美团。实习期间体感还是不错的,5月初去的,去了就一直急着要需求做,担心因为没有产出导致转正失败,在第二个星期就和mt透露我希望能够留用。虽然第一个由于美团新人landing的友好性基本没做什么需求,但是后面也写出了小2w行的代码量(不包含单测)。中期经常主动加班赶需求,经常持续一两个星期加班到10点甚至更后面。mt对我确实不错,也是言传身教,实习期间给我讲了很多关于单测,ddd,set化等的理解,也是受益匪浅,此外在做需求的时候,也能看出把比较有含金量的部分交给我做...
菜菜菜小白菜菜菜:我在字节实习了四个月,有转正的压力所以周末大部分也在公司自学,也是因为一些原因转正拖的很久,这个点还没答辩,过段时间才回去答辩。整个不确定性的焦虑贯穿了我的秋招三个月,我也曾经犹豫过是不是应该放弃转正走秋招更快,最后因为沉没成本一直舍不得放弃,前前后后七个月真的挺累的,尤其是没有来字节实习的同学已经校招拿到意向时更加焦虑。这段时间也跟mentor聊了很多次,他告诉我未来工作上或者生活上,比这些更头疼的事情会更多,关键还是要调整好自己的心态。转正没有通过从过程上来看其实跟你自身没太大的关系,拖了三个月不出结果显然是ld的问题,并且今年美团最近的开奖大家似乎都不是很乐观,所以不去也罢。我在字节实习的时候,6月份有一个赶上春招末期的25届同事刚面进来,也拿到了小sp的薪水。不要对这件事有太大的压力,时代的问题罢了
投递美团等公司10个岗位
点赞 评论 收藏
分享
09-17 19:25
已编辑
太原理工大学 游戏测试
叁六玖:公司名发我,我要这个HR带我打瓦
我的秋招日记
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务