本博客rss订阅地址: http://feed.cnblogs.com/blog/u/147990/rss

LeetCode:Palindrome Partitioning,Palindrome Partitioning II

LeetCode:Palindrome Partitioning

题目如下:(把一个字符串划分成几个回文子串,枚举所有可能的划分)

Given a string s, partition s such that every substring of the partition is a palindrome.

Return all possible palindrome partitioning of s.

For example, given s = "aab",
Return

[ ["aa","b"], ["a","a","b"] ]

 

分析:首先对字符串的所有子串判断是否是回文,设f[i][j] = true表示以i为起点,长度为j的子串是回文,等于false表示不是回文,那么求f[i][j]的动态规划方程如下:

  • 当j = 1,f[i][j] = true;

  • 当j = 2,f[i][j] = (s[i]==s[i+1]),其中s是输入字符串

  • 当j > 2, f[i][j] = f[i+1][j-2] && (s[i] == s[i+j-1])(即判断s[m..n]是否是回文时:只要s[m+1...n-1]是回文并且s[m] = s[n],那么它就是回文,否则不是回文)

这一题也可以不用动态规划来求f,可以用普通的判断回文的方式判断每个子串是否为回文。                                                                                                               本文地址

求得f后,根据 f 可以构建一棵树,可以通过DFS来枚举所有的分割方式,代码如下:

 1 class Solution {
 2 public:
 3     vector<vector<string>> partition(string s) {
 4         // IMPORTANT: Please reset any member data you declared, as
 5         // the same Solution instance will be reused for each test case.
 6         vector< vector<string> >res;
 7         int len = s.length();
 8         if(len == 0)return res;
 9         //f[i][j] = true表示以i为起点,长度为j的子串是回文
10         bool **f = new bool*[len];
11         for(int i = 0 ; i < len; i++)
12         {
13             f[i] = new bool[len+1];
14             for(int j = 0; j < len+1; j++)
15                 f[i][j] = 0;
16             f[i][1] = true;
17         }
18         for(int k = 2; k <= len; k++)
19         {
20             for(int i = 0; i <= len-k; i++)
21             {
22                 if(k == 2)f[i][2] = (s[i] == s[i+1]);
23                 else f[i][k] = f[i+1][k-2] && (s[i] == s[i+k-1]);
24             }
25         }
26         vector<string> tmp;
27         DFSRecur(s, f, 0, res, tmp);
28         for(int i = 0 ; i < len; i++)
29             delete [](f[i]);
30         delete []f;
31         return res;
32     }
33     
34     void DFSRecur(const string &s, bool **f, int i, 
35             vector< vector<string> > &res, vector<string> &tmp)
36     {//i为遍历的起点
37         int len = s.length();
38         if(i >= len){res.push_back(tmp); return;}
39         for(int k = 1; k <= len - i; k++)
40             if(f[i][k] == true)
41             {
42                 tmp.push_back(s.substr(i, k));
43                 DFSRecur(s, f, i+k, res, tmp);
44                 tmp.pop_back();
45             }
46                 
47     }
48 };

LeetCdoe:Palindrome Partitioning II

题目如下:(在上一题的基础上,找出最小划分次数)

Given a string s, partition s such that every substring of the partition is a palindrome.

Return the minimum cuts needed for a palindrome partitioning of s.

For example, given s = "aab",
Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut.                                                                                                          本文地址

算法1:在上一题的基础上,我们很容易想到的是在DFS时,求得树的最小深度即可(遍历时可以根据当前求得的深度进行剪枝),但是可能是递归层数太多,大数据时运行超时,也贴上代码:

 1 class Solution {
 2 public:
 3     int minCut(string s) {
 4         // IMPORTANT: Please reset any member data you declared, as
 5         // the same Solution instance will be reused for each test case.
 6         int len = s.length();
 7         if(len <= 1)return 0;
 8         //f[i][j] = true表示以i为起点,长度为j的子串是回文
 9         bool **f = new bool*[len];
10         for(int i = 0 ; i < len; i++)
11         {
12             f[i] = new bool[len+1];
13             for(int j = 0; j < len+1; j++)
14                 f[i][j] = 0;
15             f[i][1] = true;
16         }
17         for(int k = 2; k <= len; k++)
18         {
19             for(int i = 0; i <= len-k; i++)
20             {
21                 if(k == 2)f[i][2] = (s[i] == s[i+1]);
22                 else f[i][k] = f[i+1][k-2] && (s[i] == s[i+k-1]);
23             }
24         }
25         int res = len, depth = 0;
26         DFSRecur(s, f, 0, res, depth);
27         for(int i = 0 ; i < len; i++)
28             delete [](f[i]);
29         delete []f;
30         return res - 1;
31     }
32     void DFSRecur(const string &s, bool **f, int i, 
33             int &res, int &currdepth)
34     {
35         int len = s.length();
36         if(i >= len){res = res<=currdepth? res:currdepth; return;}
37         for(int k = 1; k <= len - i; k++)
38             if(f[i][k] == true)
39             {
40                 currdepth++;
41                 if(currdepth < res)
42                     DFSRecur(s, f, i+k, res, currdepth);
43                 currdepth--;
44             }
45                 
46     }
47 
48 };
View Code

算法2:设f[i][j]是i为起点,长度为j的子串的最小分割次数,f[i][j] = 0时,该子串是回文,f的动态规划方程是:

f[i][j] = min{f[i][k] + f[i+k][j-k] +1} ,其中 1<= k <j

这里f充当了两个角色,一是记录子串是否是回文,二是记录子串的最小分割次数,可以结合上一题的动态规划方程,算法复杂度是O(n^3), 还是大数据超时,代码如下:

 1 class Solution {
 2 public:
 3     int minCut(string s) {
 4         // IMPORTANT: Please reset any member data you declared, as
 5         // the same Solution instance will be reused for each test case.
 6         int len = s.length();
 7         if(len <= 1)return 0;
 8         //f[i][j] = true表示以i为起点,长度为j的子串的最小切割次数
 9         int **f = new int*[len];
10         for(int i = 0 ; i < len; i++)
11         {
12             f[i] = new int[len+1];
13             for(int j = 0; j < len+1; j++)
14                 f[i][j] = len;
15             f[i][1] = 0;
16         }
17         for(int k = 2; k <= len; k++)
18         {
19             for(int i = 0; i <= len-k; i++)
20             {
21                 if(k == 2 && s[i] == s[i+1])f[i][2] = 0;
22                 else if(f[i+1][k-2] == 0 &&s[i] == s[i+k-1])f[i][k] = 0;
23                 else 
24                 {
25                     for(int m = 1; m < k; m++)
26                         if(f[i][k] > f[i][m] + f[i+m][k-m] + 1)
27                             f[i][k] = f[i][m] + f[i+m][k-m] + 1;
28                 }
29             }
30         }
31         int res = f[0][len], depth = 0;
32         for(int i = 0 ; i < len; i++)
33             delete [](f[i]);
34         delete []f;
35         return res;
36     }
37 };
View Code

算法3:同上一题,用f来记录子串是否是回文,另外优化最小分割次数的动态规划方程如下,mins[i] 表示子串s[0...i]的最小分割次数:

  • 如果s[0...i]是回文,mins[i] = 0
  • 如果s[0...i]不是回文,mins[i] = min{mins[k] +1 (s[k+1...i]是回文)  或  mins[k] + i-k  (s[k+1...i]不是回文)} ,其中0<= k < i

代码如下,大数据顺利通过,结果Accept:                                                                                                                                 本文地址

 1 class Solution {
 2 public:
 3     int minCut(string s) {
 4         // IMPORTANT: Please reset any member data you declared, as
 5         // the same Solution instance will be reused for each test case.
 6         int len = s.length();
 7         if(len <= 1)return 0;
 8         //f[i][j] = true表示以i为起点,长度为j的子串是回文
 9         bool **f = new bool*[len];
10         for(int i = 0 ; i < len; i++)
11         {
12             f[i] = new bool[len+1];
13             for(int j = 0; j < len+1; j++)
14                 f[i][j] = false;
15             f[i][1] = true;
16         }
17         int mins[len];//mins[i]表示s[0...i]的最小分割次数
18         mins[0] = 0;
19         for(int k = 2; k <= len; k++)
20         {
21             for(int i = 0; i <= len-k; i++)
22             {
23                 if(k == 2)f[i][2] = (s[i] == s[i+1]);
24                 else f[i][k] = f[i+1][k-2] && (s[i] == s[i+k-1]);
25             }
26             if(f[0][k] == true){mins[k-1] = 0; continue;}
27             mins[k-1] = len - 1;
28             for(int i = 0; i < k-1; i++)
29             {
30                 int tmp;
31                 if(f[i+1][k-i-1] == true)tmp = mins[i]+1;
32                 else tmp = mins[i]+k-i-1;
33                 if(mins[k-1] > tmp)mins[k-1] = tmp;
34             }
35         }
36         for(int i = 0 ; i < len; i++)
37             delete [](f[i]);
38         delete []f;
39         return mins[len-1];
40     }
41 
42 };

 【版权声明】转载请注明出处:http://www.cnblogs.com/TenosDoIt/p/3421283.html

posted @ 2013-11-13 13:56  tenos  阅读(1559)  评论(0编辑  收藏  举报

本博客rss订阅地址: http://feed.cnblogs.com/blog/u/147990/rss

公益页面-寻找遗失儿童