微软面试题: LeetCode 5. 最长回文子串 出现次数:1

方法一     中心扩散法

解析: 回文串一定是 中心对称的,回文串的对称中心可能是 1 个字符,也可能是 2个字符,遍历 s 中的 字符

分别以 s[i] 和s[i] 、s[i+1]  为中心像两边扩散,记录 最长 回文串的 起始位置 和长度。

时间 O(n ^ 2)  空间 O(1)

代码: 

 1 //方法一: 中心扩散法       时间 O(n ^ 2)  空间:O(1)    
 2     string CentralDiffusion(string s)
 3     {
 4         int sub_len = 1;
 5         int start = 0;
 6        
 7         for(int i = 0; i < s.size();++i)
 8         {
 9             int l,r,sub_len_tmp;
10             //考虑以(s[i],s[i+1])为回文串中心的情况 
11             if(i + 1 < s.size() && s[i] == s[i+1])
12             {
13                 l = i;
14                 r = i + 1;
15                 sub_len_tmp = 0;
16                 while(l >= 0 && r < s.size() && s[l] == s[r])
17                 {
18                     sub_len_tmp += 2;
19                     --l;
20                     ++r;
21                 }
22                 if(sub_len_tmp > sub_len)
23                 {
24                     start = l + 1;
25                     sub_len = sub_len_tmp;
26                 }
27             }
28             //考虑以s[i]为奇数回文串中心的情况 
29             l = i - 1;
30             r = i + 1;
31             sub_len_tmp = 1;
32             while(l >= 0 && r < s.size() && s[l] == s[r])
33             {
34                 sub_len_tmp += 2;
35                 --l;
36                 ++r;
37             }
38             if(sub_len_tmp > sub_len)
39             {
40                 start = l + 1;
41                 sub_len = sub_len_tmp;
42             }
43         }
44         return s.substr(start,sub_len);
45     }

方法二    动态规划

分析:

1 个字符 s[i] 的子串一定是回文串,

2个字符 s[ i : i + 1] 当 s[i] == s[i+1] 时 是回文串,否则不是,

3 个字符的子串 s[i : i + 2]  : 当 s[i] == s[i+2] 时是回文子串,此时 s[i : i + 2]  内层的 s[i+1:i+1] 只有一个字符,一定是回文的。

4 个字符 s[i : i+3]  :  当 s[i] == s[i+3] 时是回文子串 而且 其内层 s[i+1:i+2] 也是回文串时, s[i : i+3]  是回文串。

........

1.  定义状态  dp[ i ][ j ]  ,   等于 1    表示 子串 s[ i : j ] 是回文串,等于 0 不是回文串。 0<= j < n, 0 <= i <=j; 

2.  状态转移方程    dp[ i ][ j ] = dp[ i + 1][ j - 1] == 1 && s[i] == s[j] ?1:0;

3.  设置初始状态   dp[ i ][ i ] = 1, dp[ i ][ i + 1] =  s[ i ] == s [ i + 1 ] ? 1 : 0;

 1 //方法二: 动态规划   时间 O(n ^ 2)  空间:O(n)    
 2 // dp 的方法虽然时间复杂度也是 O(n ^ 2),但是和中心扩散法相比,避免了很多重复计算,有很好的的性能提升
 3      string dp(string s)
 4      {
 5           const int s_len = s.size();
 6           if(s_len <= 1)
 7           {
 8               return s;
 9           }
10           int start = 0;
11           int sub_len = 1;
12           //定义状态 dp[i][j] , 等于 1 表示s[i:j]是回文子串,等0不是
13           int dp[s_len][s_len] ;
14           memset( dp, 0, sizeof(dp));
15           for(int i = 0; i < s_len; ++i)// dp base case
16           {
17               dp[i][i] = 1;
18               if(i+1 < s_len && s[i] == s[i+1])
19               {
20                  dp[i][i+1] = 1;
21                  start = i;
22                  sub_len = 2;
23               }
24           }
25           for(int j = 1; j < s_len;++j)
26           {
27               for(int i = 0; i < j - 1;++i) //dp[k][k] 和 dp[k][k+1] 作为base case    
28               {
29                   if(dp[i+1][j-1] == 1 && s[i] == s[j])//内一层子串是回文的,外层两个字符相同
30                   {
31                       if(j - i + 1 > sub_len)
32                       {
33                           start = i;
34                           sub_len = j - i + 1;
35                       }
36                       dp[i][j] = 1;//标记 s[i:j] 为回文子串
37                   }
38               }
39           }
40           return s.substr(start,sub_len);
41      }

 Tips : 在记录子串信息时,记录 子串的起始位置 start 和 子串长度 sub_len 就行,最后再做截取操作。

原文地址:https://www.cnblogs.com/wangxf2019/p/14688312.html