UVA 11584 "Partitioning by Palindromes"(DP+Manacher)

传送门

 

题意  

思路一

  定义 dp[i] 表示 0~i 的最少划分数;

  首先,用马拉车算法求解出回文半径数组;

  对于第 i 个字符 si,遍历 j (0 ≤ j < i),判断以 j 为回文中心的最大回文串是否包含 si

  如果包含,dp[ i ]=min{dp[ i ],dp[2*j-i-1]+1};

Code

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e3+50;
 4 
 5 char t[maxn];
 6 int r[maxn<<1];
 7 
 8 struct Manacher
 9 {
10     char s[maxn<<1];
11     void Init(char *ss,int len)
12     {
13         int index=0;
14         s[index++]='#';
15         for(int i=0;i < len;++i)
16         {
17             s[index++]=ss[i];
18             s[index++]='#';
19         }
20         s[index]='\0';
21     }
22     void mana(char *ss)
23     {
24         Init(ss,strlen(ss));
25         int len=strlen(s);
26         int R=-1;
27         int C;
28         for(int i=0;i < len;++i)
29         {
30             r[i]=R > i ? min(R-i+1,r[2*C-i]):1;
31             for(;i-r[i] >= 0 && i+r[i] < len && s[i-r[i]] == s[i+r[i]];r[i]++);
32             if(i+r[i] > R)
33             {
34                 R=i+r[i]-1;
35                 C=i;
36             }
37         }
38     }
39 }_mana;
40 
41 int dp[maxn];
42 int Solve()
43 {
44     _mana.mana(t);
45 
46     dp[0]=1;
47     int len=strlen(t);
48     for(int i=1;i < len;++i)
49     {
50         dp[i]=dp[i-1]+1;
51         for(int j=0;j <= 2*i;++j)
52         {
53             
54             ///t中的第i个字符在预处理后的s数组中的位置为2*i+1
55             ///因为可能由偶回文的情况,所以需要用到'#'
56             ///直接判断在s数组中j的对应的最大回文j+r[j]是否包含2*i+1
57             ///如果包含,再找到2*i+1以j为中心的对称点2*j-(2*i+1)
58             ///判断2*j-(2*i+1)对应于t中的位置的前一个位置(2*j-(2*i+1))/2-1是否在[0,len-1]范围内
59             ///如果在,更新dp[i]
60             int cur=j+r[j];
61             int index=(2*j-2*i-1)/2-1;
62             if(2*i+1 < cur)
63                 dp[i]=min(dp[i],1+(index >= 0 ? dp[index]:0));
64         }
65     }
66     return dp[len-1];
67 }
68 int main()
69 {
70 //    freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin);
71     int test;
72     scanf("%d",&test);
73     while(test--)
74     {
75         scanf("%s",t);
76         printf("%d\n",Solve());
77     }
78     return 0;
79 }
View Code 

思路二(reference from zishu)

  定义dp[ i ]表示0~i划分成的最小回文串的个数,则dp[ i ]=min{d[ j ]+1 | j ≤ i && t[ j+1,....,i ]为回文串};

code

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e3+50;
 4 
 5 char t[maxn];
 6 char s[maxn<<1];
 7 bool isPal[maxn][maxn];
 8 int dp[maxn];
 9 
10 void Init()///O(n^2)预处理出t[i,..,j]是否为回文串
11 {
12     int len=strlen(t);
13     for(int i=0;i < len;++i)
14         for(int j=0;j < len;++j)
15             isPal[i][j]=false;
16     int index=0;
17     s[index++]='#';
18     for(int i=0;i < len;++i)
19     {
20         s[index++]=t[i];
21         s[index++]='#';
22     }
23     s[index]='\0';
24 
25     for(int i=0;i < index;++i)
26     {
27         int r=0;
28         while(i-r >= 0 && i+r < index && s[i-r] == s[i+r])
29         {
30             if((i-r)&1)
31                 isPal[(i-r)/2][(i+r)/2]=true;
32             r++;
33         }
34     }
35 }
36 
37 int Solve()
38 {
39     Init();
40     int len=strlen(t);
41     dp[0]=1;
42     for(int i=1;i < len;i++)
43     {
44         dp[i]=dp[i-1]+1;
45         for(int j=0;j < i;++j)
46             if(isPal[j][i])
47                 dp[i]=min(dp[i],1+(j > 0 ? dp[j-1]:0));
48     }
49     return dp[len-1];
50 }
51 int main()
52 {
53 //    freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin);
54     int test;
55     scanf("%d",&test);
56     while(test--)
57     {
58         scanf("%s",t);
59         printf("%d\n",Solve());
60     }
61     return 0;
62 }
View Code

 

posted @ 2019-06-11 09:05  HHHyacinth  阅读(153)  评论(0编辑  收藏  举报