【题意】
你可以产生一个回文串,也可以将两个串合并成一个串,问产生目标串需要的最少合并次数。
【思路】
显然我们要先产生目标串中包含的极大回文字符串。
Manacher求出每个位置可以向两边延伸的最长回文串。
则题目转化为有若干条线段,求最少的线段将[1..n]覆盖。贪心DP皆可上,DP需要BIT优化一下。
【代码】
1 #include<set> 2 #include<cmath> 3 #include<queue> 4 #include<vector> 5 #include<cstdio> 6 #include<cstring> 7 #include<iostream> 8 #include<algorithm> 9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt) 10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 11 using namespace std; 12 13 typedef long long ll; 14 const int N = 2e5+10; 15 const int inf = 1e9; 16 17 struct Node 18 { 19 int l,r; 20 bool operator < (const Node& rhs) const 21 { 22 return r<rhs.r; 23 } 24 } q[N]; 25 int tot; 26 27 char s[N],a[N]; 28 int n,m,p[N]; 29 30 int C[N]; 31 void upd(int x,int v) 32 { 33 for(int i=x;i;i-=i&(-i)) 34 C[i]=min(C[i],v); 35 } 36 int query(int x) 37 { 38 if(x==0) return 0; 39 int res=inf; 40 for(int i=x;i<=n;i+=i&(-i)) 41 res=min(res,C[i]); 42 return res; 43 } 44 45 void Add(int l,int r) 46 { 47 l=l/2+1,r=r/2-1; 48 if(l>r) return ; 49 q[++tot]=(Node){l,r}; 50 } 51 void Manacher() 52 { 53 m=2*n+1; 54 for(int i=1;i<=n;i++) 55 { 56 a[i<<1]=s[i]; 57 a[i<<1|1]='#'; 58 } 59 a[0]='+',a[m+1]='-',a[1]='#'; 60 int mx=0,id; 61 for(int i=1;i<=m;i++) 62 { 63 if(mx>i) p[i]=min(mx-i,p[id*2-i]); 64 else p[i]=1; 65 while(a[i-p[i]]==a[i+p[i]]) p[i]++; 66 Add(i-p[i],i+p[i]); 67 if(p[i]+i>mx) mx=i+p[i],id=i; 68 } 69 } 70 71 int dp() 72 { 73 int ans=inf; 74 sort(q+1,q+tot+1); 75 FOR(i,1,tot) 76 { 77 int x=query(q[i].l-1)+1; 78 upd(q[i].r,x); 79 if(q[i].r==n) ans=min(ans,x); 80 } 81 return ans; 82 } 83 84 int main() 85 { 86 while(scanf("%s",s+1)==1) 87 { 88 memset(p,0,sizeof(p)); 89 tot=0; 90 n=strlen(s+1); 91 FOR(i,1,n) C[i]=inf; 92 Manacher(); 93 printf("%d ",dp()-1); 94 } 95 return 0; 96 }