BZOJ 3790 神奇项链 hash/后缀自动机+贪心

Description

母亲节就要到了,小 H 准备送给她一个特殊的项链。这个项链可以看作一个用小写字母组成的字符串,每个小写字母表示一种颜色。

为了制作这个项链,小 H 购买了两个机器。第一个机器可以生成所有形式的回文串,第二个机器可以把两个回文串连接起来,而且第二个机器还有一个特殊的性质:假如一个字符串的后缀和一个字符串的前缀是完全相同的,那么可以将这个重复部分重叠。例如:aba和aca连接起来,可以生成串abaaca或 abaca。

现在给出目标项链的样式,询问你需要使用第二个机器多少次才能生成这个特殊的项链。

Input

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

Output

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

Sample Input

abcdcba

abacada

abcdef

Sample Output

0

2

5

HINT

每个测试数据,输入不超过 5行,每行的字符串长度小于等于 50000。

分析:

回文串之间可以相互重叠,但是不妨碍辨认这些回文串,因此问题就变成了在给出的字符串中找到数量最少的回文串覆盖整个字符串。

一个字符串看成一条线段,经典的贪心问题,只需要再求出每个点为中心的最长回文串即可进行贪心(小技巧是在字符串的间隔插上一个分隔符)。

最后用了二分+hash来求这个东西,想了想还可以用回文自动机把串倒着插进去来进行贪心。(bzoj不能用gets真的是太可怕了)

于是我把两份代码都拿出来了(滑稽)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<queue>
 8 #include<set>
 9 #include<map>
10 #include<vector>
11 #include<cctype>
12 using namespace std;
13 const int maxn=100005;
14 typedef long long LL;
15 const int mo=2100000007;
16
17 char S[maxn],T[maxn];
18 int n,hash1[maxn],hash2[maxn],pw[maxn];
19 struct data{ int l,r; }line[maxn];
20
21 bool check(int i,int mid)
22 {
23     int v1=i-mid==0?hash1[i]:(hash1[i]-1ll*hash1[i-mid-1]*pw[mid+1]%mo+mo)%mo;
24     int v2=i+mid==n-1?hash2[i]:(hash2[i]-1ll*hash2[i+mid+1]*pw[mid+1]%mo+mo)%mo;
25     return v1==v2;
26 }
27 bool cmp(data x,data y){ return x.l<y.l||x.l==y.l&&x.r<y.r; }
28 void work()
29 {
30     n=strlen(T);
31     hash1[0]=T[0]-‘A‘+1;
32     for(int i=1;i<n;i++) hash1[i]=(hash1[i-1]*89ll+T[i]-‘A‘+1)%mo;
33     hash2[n-1]=T[n-1]-‘A‘+1;
34     for(int i=n-2;i>=0;i--) hash2[i]=(hash2[i+1]*89ll+T[i]-‘A‘+1)%mo;
35     for(int i=0;i<n;i++){
36         int L=0,R=min(i,n-i-1)+1,mid,len=0;
37         while(L<R){
38             mid=L+R>>1;
39             if(check(i,mid)) len=mid,L=mid+1;
40             else R=mid;
41         }
42         line[i]=(data){i-len,i+len};
43     }
44     sort(line,line+n,cmp);
45     int ans=0,pos=0,mx=0;
46     for(int i=0;i<n;i++){
47         if(line[i].l>pos) pos=mx,mx=0,ans++;
48         if(line[i].r>mx) mx=line[i].r;
49     }
50     printf("%d\n",max(0,ans-1));
51 }
52 int main()
53 {
54     pw[0]=1;
55     for(int i=1;i<=100002;i++) pw[i]=pw[i-1]*89ll%mo;
56     while(scanf("%s",S)==1){
57         int cnt=0; n=strlen(S);
58         T[cnt++]=‘A‘;
59         for(int i=0;i<n;i++)
60             T[cnt++]=S[i],T[cnt++]=‘A‘;
61         T[cnt]=‘\0‘;
62         work();
63     }
64     return 0;
65 }
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<queue>
 8 #include<set>
 9 #include<map>
10 #include<vector>
11 #include<cctype>
12 #include<ctime>
13 using namespace std;
14 const int maxl=50005;
15
16 char S[maxl];
17 int r[maxl];
18 struct PAM{
19     static const int maxn=50005;
20     int sz,last,to[maxn][26],len[maxn],fail[maxn],s[maxn],n;
21     int newnode(int l){
22         memset(to[sz],0,sizeof(to[sz]));
23         len[sz]=l,fail[sz]=0;
24         return sz++;
25     }
26     void init(){
27         sz=last=n=0;
28         newnode(0);newnode(-1);
29         s[0]=-1,fail[0]=1;
30     }
31     int getfail(int p){
32         while(s[n]!=s[n-len[p]-1]) p=fail[p];
33         return p;
34     }
35     void extend(int w){
36         s[++n]=w;
37         int p=getfail(last);
38         if(!to[p][w]){
39             int np=newnode(len[p]+2);
40             fail[np]=to[getfail(fail[p])][w];
41             to[p][w]=np;
42         }
43         last=to[p][w];
44     }
45 }pam;
46
47 int main()
48 {
49     while(scanf("%s",&S)==1){
50         pam.init();
51         int n=strlen(S);
52         for(int i=n-1;i>=0;i--){
53             pam.extend(S[i]-‘a‘);
54             r[i]=i+pam.len[pam.last];
55         }
56         int pos=0,mx=0,ans=0;
57         for(int i=0;i<n;i++){
58             if(i>pos) pos=mx,mx=0,ans++;
59             if(r[i]>mx) mx=r[i];
60             if(i==n-1&&pos==n-1) ans++;
61         }
62         printf("%d\n",max(0,ans-1));
63     }
64     return 0;
65 }

原文地址:https://www.cnblogs.com/KKKorange/p/8724463.html

时间: 2024-10-29 19:07:37

BZOJ 3790 神奇项链 hash/后缀自动机+贪心的相关文章

BZOJ 3790 神奇项链 Hash+二分

题目大意:给出一个字符串,求出这是最少由多少个回文串组成的.回文串可以重叠. 思路:将原串中的所有回文串都统计出来,然后变成一些区间,问题就转化成了区间并的问题. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 400010 #define BASE 1333 #define INF 0x3f3f3f3f using

BZOJ 3790 神奇项链 Hash+二分+树状数组

题目大意:给定一个串,问这个串最少可以由回文串拼接多少次而成(拼接可以重叠) 首先将每两个字符之间插入占位符,然后Hash+二分搞出所有极大回文串(可以用manacher,我不会) 问题转化成了给定一些区间,求最少的能覆盖整个数轴的区间 将所有区间按照某一端点排序 然后上树状数组即可 回头还是去学学manacher吧... #include <cstdio> #include <cstring> #include <iostream> #include <algo

bzoj 3790 神奇项链(Manacher,DP+BIT | 贪心)

[题意] 你可以产生一个回文串,也可以将两个串合并成一个串,问产生目标串需要的最少合并次数. [思路] Manacher求出每个位置可以向两边延伸的最长回文串. 则题目转化为有若干条线段,求最少的线段将[1..n]覆盖.贪心DP皆可上,DP需要BIT优化一下. [代码] 1 #include<set> 2 #include<cmath> 3 #include<queue> 4 #include<vector> 5 #include<cstdio>

BZOJ 3790 神奇项链(manacher+DP+树状数组)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3790 [题目大意] 问最少用几个回文串可以构成给出串,重叠部分可以合并 [题解] 我们先用manacher处理出每个位置最长的回文串, 那么题目就转化为求最少的线段来覆盖全区间,那就是经典的dp题了, dp[i]=min(dp[j]+1)(i线段的左端点-1和j线段的右端点有交) 用树状数组优化一下即可. [代码] #include <cstdio> #include <al

BZOJ 2946 Poi2000 公共串 后缀自动机

题目大意:求n个串的最长公共子串 太久没写SAM了真是-- 将第一个串建成后缀自动机,用其它的串进去匹配 每个节点记录每个串在上面匹配的最大长度 那么这个节点对答案的贡献就是所有最大长度的最小值 对所有贡献取最大就行了= = 这最大最小看着真是别扭 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 10100 using namesp

BZOJ 3238 [Ahoi2013]差异(后缀自动机)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3238 [题目大意] 给出一个串,设T[i]表示从第i位开始的后缀, 求sum(len(T[i])+len(T[j])-2*lcp(T[i],T[j])) [题解] 根据反串的后缀自动机建立后缀树, 则两点的LCA在自动机中的length就是他们的LCP, 树形DP统计一下即可. [代码] #include <cstdio> #include <algorithm> #i

BZOJ 2946 POI2000 公共串 后缀自动机(多串最长公共子串)

题意概述:给出N个字符串,每个串的长度<=2000(雾...可能是当年的年代太久远机子太差了),问这N个字符串的最长公共子串长度为多少.(N<=5) 抛开数据结构,先想想朴素做法. 设计一种稳定的暴力算法.可以想到这样一种做法:首先确定一个串,枚举每个位置,然后暴力计算其他每个串以这个位置开头的最长匹配,取最小值,就是在公共子串在我们确定下来的串的这个位置开头的时候所能得到的最长公共子串.不难发现把这个问题转化成后缀的形式也是一样的.同时发现可能在枚举多个位置的时候答案甚至最后构造出来的串都是

BZOJ 1396&amp;&amp;2865 识别子串[后缀自动机 线段树]

Description 在这个问题中,给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件: 1.i≤K≤j. 2.子串T只在S中出现过一次. 例如,S="banana",K=5,则关于第K位的识别子串有"nana","anan","anana","nan","banan"和"banana". 现在,给定S,求对于S的

【BZOJ】2555: SubString(后缀自动机)

http://www.lydsy.com/JudgeOnline/problem.php?id=2555 学到了如何快速维护right值orz (虽然这仍然是暴力维护,但是这是O(n)的暴力233 首先我们在加一个新节点的时候直接遍历它的parent树就行啦啦!!!!这样直接就暴力维护了...我竟然没想到.. 然后在复制新节点的时候不要忘记将right也要赋值进去.. 然后就是裸题了 #include <cstdio> #include <cstring> #include <