bzoj3790: 神奇项链(二分+hash)

该博客介绍了如何利用二分查找和Hash技术来解决神奇项链问题,即找到生成特定目标串所需的最少串拼接次数。通过分析回文串特性,并结合贪心策略,可以有效降低使用第二个机器的次数。
摘要由CSDN通过智能技术生成

传送门
题意:
有两个机器:一个可以生成一个回文串,另一个可以把两个串拼接起来。如果(令待拼接的两个串为A,B)A的一个后缀和B的一个前缀相同,那么可以将这个重复部分重叠。
给一个目标串,求为了得到这个目标串,至少需要使用多少次第二个机器。
思路:
m a n a c h e r / manacher/ manacher/二分+ h a s h hash hash求出极长回文串然后贪心即可。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int N=1e5+5;
char s[N];
int n;
typedef pair<int,int> pii;
typedef unsigned long long Ull;
typedef long long ll;
const Ull bas=141;
const int mod=1e9+9;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
struct string_hash{
    Ull pw1[N],s1[N];
    int pw2[N],s2[N];
    inline void init(){
        pw1[0]=pw2[0]=1;
        for(ri i=1;i<=n;++i)pw1[i]=pw1[i-1]*bas,pw2[i]=mul(pw2[i-1],bas);
        for(ri i=1;i<=n;++i)s1[i]=s1[i-1]*bas+(s[i]-'a'+15),s2[i]=add(mul(s2[i-1],bas),(s[i]-'a'+46));
    }
    inline Ull get1(int l,int r){return s1[r]-s1[l-1]*pw1[r-l+1];}
    inline int get2(int l,int r){return dec(s2[r],mul(s2[l-1],pw2[r-l+1]));}
}S1,S2;
inline bool check(int l,int r){return S1.get1(l,r)==S2.get1(n-r+1,n-l+1)&&S1.get2(l,r)==S2.get2(n-r+1,n-l+1);}
vector<pii>a; 
inline void Init(){
    S1.init();
    reverse(s+1,s+n+1);
    S2.init();
    reverse(s+1,s+n+1); 
    a.clear();
    for(ri L,R,l,r,res,i=1;i<=n;++i){
        L=max(1,2*i-n),R=i,res=i;
        while(L<=R){
            int mid=L+R>>1;
            if(check(mid,2*i-mid))res=mid,R=mid-1;
            else L=mid+1;
        }
        a.push_back(pii(res,2*i-res));
    }
    for(ri L,R,l,r,res,i=1;i<n;++i){
        if(s[i]!=s[i+1])continue;
        L=max(1,2*i+1-n),R=i,res=i;
        while(L<=R){
            int mid=L+R>>1;
            if(check(mid,2*i+1-mid))res=mid,R=mid-1;
            else L=mid+1;
        }
        a.push_back(pii(res,2*i+1-res));        
    }
}
inline bool cmp(const pii&a,const pii&b){return a.fi^b.fi?a.fi<b.fi:a.se>b.se;}
int main(){
    while(~scanf("%s",s+1)){
        n=strlen(s+1);
        Init();
        sort(a.begin(),a.end(),cmp);
        int l=0,r=0,mxr=0,cnt=0;
        for(;;++r){
            mxr=max(mxr,a[r].se);
            if(r==a.size()-1||a[r+1].fi!=1)break;
        }
        for(int mx;l<a.size();l=r+1){
            r=l;
            mx=a[l].se;
            if(mxr==n)break;
            while(r!=a.size()-1&&a[r+1].fi<=mxr+1)++r,mx=max(mx,a[r].se);
            ++cnt;
            mxr=mx;
        }
        cout<<cnt<<'\n';
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值