Codeforces 30E Tricky and Clever Password-字符串匹配

4 篇文章 0 订阅
4 篇文章 0 订阅

传送门

题意:

密码是一个长度为奇数的回文串,现在我们对这个密码进行加密:把密码分成 3 段,最前面的 X 个字符为一段,最后面的 X 个字符为一段,剩余的字符为一段。这三段依次称之为 prefix, suffix, middle 。middle 的长度为一个大于 0 的奇数, prefix 、 suffix 的长度相等。加密后的密码即为 A + prefix + B + middle + C + suffix ,其中 A 、 B 、 C 是三个字符串,且都有可能为空, + 表示字符串相连。

先给出加密后的长度为n的字符串,求最长可能的密码串。

(n<=1e5)

Solution:

第一想法就是枚举middle然后计算最长的prefix和suffix

观察发现只需枚举以每个位置为中心的最长的回文串即可

发现suffix就是加密串的后缀,将suffix翻转后合prefix相同,那么我们就可以翻转加密串后和原串进行匹配,类似149E,定义pos[i]表示模式串中最早能匹配到第i个字符的位置,接着对于每个middle,二分prefix长度然后判断对应的suffix是否合法即可

复杂度 O(nlogn) O ( n log ⁡ n )

找回文串可以用manacher,代码里使用的是二分+hash
处理pos数组可以用KMP,代码里使用的是z-box算法

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#define ull unsigned long long
using namespace std;
const int N=100010;
const int bas=211;
ull ha[N],fha[N],mi[N];
int n,z[2*N],cnt,pos[2*N];
struct huiwen{
    int l,r;
}q[N];
char s[2*N];
int ans,pans[4],lans[4];
ull gha(int l,int r)
{
    return ha[r]-ha[l-1]*mi[r-l+1];
}
ull fgha(int l,int r)
{
    return fha[l]-fha[r+1]*mi[r-l+1];
}
void getpos(int i)
{
    int r=min(i,n-i-1),l=0,ans=0;
    while (l<=r)
    {
        int mid=l+r>>1;
        if (gha(i-mid,i)==fgha(i,i+mid)) ans=mid,l=mid+1;
        else r=mid-1;
    }
    q[i].l=i-ans,q[i].r=i+ans;
}
void findhw()
{
    for (int i=0;i<n;i++)
        getpos(i);
}
void zalo()
{
    int l=0,r=0;
    for (int i=1;i<=2*n;i++)
    {
        if (i<=n)pos[i]=1e9;
        if (i>r)
        {
            int t=i,bg=0;
            while (s[t]==s[bg]) t++,bg++;
            z[i]=bg;
            l=i,r=t-1;
        }
        else
        {
            if (i+z[i-l]-1<r) z[i]=z[i-l];
            else
            {
                int t=r+1,bg=r-i+1;
                while (s[t]==s[bg]) t++,bg++;
                z[i]=bg;
                l=i,r=t-1;
            }
        }
        if (i>n) pos[z[i]]=min(pos[z[i]],i+z[i]-1-n);
    }
    for (int i=n-1;i>=1;i--) pos[i]=min(pos[i],pos[i+1]-1);
}
int main()
{
    scanf("%s",s);
    n=strlen(s);
    ha[0]=s[0];
    mi[0]=1;
    for (int i=1;i<=n;i++) mi[i]=mi[i-1]*bas;
    for (int i=1;i<n;i++) ha[i]=ha[i-1]*bas+s[i]; 
    for (int i=n-1;i;i--) fha[i]=fha[i+1]*bas+s[i];
    findhw();
    s[n]='$';
    for (int i=0;i<n;i++)
        s[i+n+1]=s[i];
    for (int i=0,j=n-1;i<j;i++,j--) swap(s[i],s[j]);
    zalo();
    for (int i=0;i<n;i++)
    {
        int l=q[i].r+1,r=n-1,as=-1;
        while (l<=r)
        {
            int mid=l+r>>1;
            if (pos[n-mid]<=q[i].l) as=mid,r=mid-1;
            else l=mid+1;
        }
        if (as==-1) {if (q[i].r-q[i].l+1>ans) ans=q[i].r-q[i].l+1,cnt=1,pans[1]=q[i].l,lans[1]=ans;}
        else
        {
            if (q[i].r-q[i].l+1+2*(n-as)>ans)
            {
                ans=q[i].r-q[i].l+1+2*(n-as);
                pans[1]=pos[n-as]-n+as,lans[1]=n-as;
                pans[2]=q[i].l,lans[2]=q[i].r-q[i].l+1;
                pans[3]=as,lans[3]=n-as;
                cnt=3;
            }
        }
    }
    printf("%d\n",cnt);
    for (int i=1;i<=cnt;i++) printf("%d %d\n",pans[i]+1,lans[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值