• BZOJ3790 神奇项链


    权限题...

    描述:


    Description

    母亲节就要到了,小 H 准备送给她一个特殊的项链。这个项链可以看作一个用小写字
    母组成的字符串,每个小写字母表示一种颜色。为了制作这个项链,小 H 购买了两个机器。第一个机器可以生成所有形式的回文串,第二个机器可以把两个回文串连接起来,而且第二个机器还有一个特殊的性质:假如一个字符串的后缀和一个字符串的前缀是完全相同的,那么可以将这个重复部分重叠。例如:aba和aca连接起来,可以生成串abaaca或 abaca。现在给出目标项链的样式,询问你需要使用第二个机器多少次才能生成这个特殊的项链。 
     

    Input

    输入数据有多行,每行一个字符串,表示目标项链的样式。 
     

    Output

    多行,每行一个答案表示最少需要使用第二个机器的次数。 
     

    Sample Input

    abcdcba 
    abacada 
    abcdef

    Sample Output

    0
    2
    5

    Hint

    每个测试数据,输入不超过 5行 

     

    每行的字符串长度小于等于 50000 

    题目就是问给定的串最少是由多少个回文串覆盖构成的

    关于回文串首先肯定想的是 manacher

    用 manacher 可以求出每个回文串

    题目问的是给定的串最少是由多少个回文串覆盖构成的

    那就变成了经典的线段覆盖问题

    贪心一波就好了:

    开一个数组 lin[ i ] 表示从第 i 个字符开始的回文串的最长长度

    首先第一个字符一定要被覆盖到

    那么就选 1~lin[ 1 ] 的线段来覆盖

    然后考虑lin[ 1 ] + 1 的点一定要被覆盖到

    枚举 2~lin[ 1 ] +1 开头的所有线段,找到右端点最右的线段并选择

    然后考虑更后面的点,用同样的方法贪心即可

    这么显然的贪心应该不用证明吧

    复杂度 O(n)

    然后答案就是选择的线段数量-1 

    代码:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int N=2e5+7;;
    char s[N],a[N];
    int f[N],ans;
    int lin[N];//注意此时的lin[i]是在原串时下标i
    void manacher()
    {
        int len=strlen(s+1);
        len=len*2+1;
        a[0]='$';
        for(int i=1;i<=len;i++)
            a[i]= (i&1) ? '#' : s[i>>1];
    
        int pos=0,mx=0;
        for(int i=1;i<=len;i++)
        {
            f[i]= i<mx ? min(f[(pos<<1)-i],mx-i) : 1;
            while(a[i+f[i]]==a[i-f[i]]) f[i]++;
            if(i+f[i]>mx) mx=i+f[i],pos=i;
        }
    }
    void slove()
    {
        int ans=0,mx=0,pos=0,len=strlen(s+1);
        for(int i=2;i<=len<<1;i++)
        {
            pos=(i>>1)-(f[i]>>1)+1;
            lin[pos]=max(lin[pos],f[i]-1);//计算出lin数组
        }
        int i=1;
        while(i<=len)
        {
            mx=max(mx,i+lin[i]-1); pos=mx;
            for(int j=i+1;j<=pos;j++)
                mx=max(mx,j+lin[j]-1);//贪心
            ans++; i=pos+1;
        }
        printf("%d
    ",ans-1);//线段数量-1
    }
    int main()
    {
        while(scanf("%s",s+1)!=EOF)
        {
            memset(f,0,sizeof(f));
            memset(a,0,sizeof(a));
            memset(lin,0,sizeof(lin));
            manacher();
            slove();
        }
        return 0;
    }

    当然因为数据不大,所以 manacher 可以换成哈希

    枚举中心然后二分可能长度,复杂度O(nlogn)

    代码就不贴了

  • 相关阅读:
    JAVA面试常见问题之开源框架和容器篇
    JAVA面试常见问题之Redis篇
    JDK8日期时间操作小汇总
    JAVA面试常见问题之数据库篇
    JAVA面试常见问题之设计模式篇
    JAVA面试常见问题之锁机制篇
    JAVA面试常见问题之进程和线程篇
    JAVA面试常见问题之常见集合篇
    JAVA面试常见问题之基础篇
    洛谷P1315 观光公交
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9701599.html
Copyright © 2020-2023  润新知