• 寻找最长回文子串


    最长回文子串的问题描述:

    给出一个字符串S,求S的最长回文子串的长度。

    样例:

    字符串"PATZJUJZTACCBCC"的最长回文子串为“ATZJUJZTA”,长度为9。

     

    先看暴力解法:枚举子串的两个端点i和j,判断在i,区间内的子串是否回文。

    从复杂度上来看,枚举端点需要O(n2),判断回文需要O(n),因此总复杂度是O(n3)。

     

    介绍动态规划的方法,使用动态规划可以达到更优的0(n2)复杂度,而最长回文子串有很多种使用动态规划的方法,这里介绍其中最容易理解的一种。

    令dp[i][j]表示S[i]至S[j]所表示的子串是否是回文子串,是则为1,不是为0。这样根据S[i]是否等于S[j],可以把转移情况分为两类:

    ①若S[i] = S[j],那么只要S[i+1]至S[j-1]是回文子串,S[i]至S[j]就是回文子串;如果S[i+1]至S[j-1]不是回文子串,则S[i]至S[j]也不是回文子串。

    ②若S[i]!=S[j],那么S[i]至S[j]一定不是回文子串。

     

    写出状态转移方程:

     

    边界:dp[i][i] = 1, dp[i][i+1] = (S(i)==S(i+1)?1:0);

     

    到这里还有一个问题没有解决,那就是如果按照i和j从小到大的顺序来枚举子串的两个端点,然后更新dp[i][j],会无法保证dp[i+1][j-1]已经被计算过,从而无法得到正确的dp[i][j]。

    先固定i=0,然后枚举j从2开始。

    当求解dp[0][2]时,将会转换为dp[1][1],而dp[1][1]是在初始化中得到的;

    当求解dp[0][3]时,将会转换为dp[1][2],而dp[1][2]也是在初始化中得到的;

    当求解dp[0][4]时,将会转换为dp[1][3],但是dp[1][3]并不是已经计算过的值,因此无法状态转移。

    事实上,无论对i和j的枚举顺序做何调整,都无法调和这个矛盾,因此必须想办法寻找新的枚举方式。

    根据递推写法从边界出发的原理,注意到边界表示的是长度为1和2的子串,且每次转移时都对子串的长度减了1,因此不妨考虑按子串的长度和子串的初始位置进行枚举,

     

    即第一遍将长度为3的子串的dp值全部求出,

    第二遍通过第一遍结果计算出长度为4的子串的dp值……这样就可以避免状态无法转移的问题。

     

    最普通的遍历O(n3)

     1 void way1()
     2 {
     3     for (int i = 0; i < str.length(); ++i)
     4     {
     5         for (int j = str.length() - 1; j > i; --j)
     6         {
     7             t1.assign(str.begin() + i, str.begin() + j + 1);
     8             t2.assign(t1.rbegin(), t1.rend());
     9             if (t1 == t2)
    10                 res = res > t1.length() ? res : t1.length();
    11         }
    12     }
    13 }

     利用回文子串中心的两边相同

     1 void way2()
     2 {
     3     for (int i = 0; i < str.size(); ++i) {
     4         int j;
     5         for (j = 1; i - j >= 0 && i + j < str.size() && str[i + j] == str[i - j]; ++j);//以当前字符为回文中心查找最长回文子串
     6         res= max(res, 2 * j - 1);//更新回文子串最大长度
     7         for (j = 0; i - j >= 0 && i + j + 1 < str.size() && str[i - j] == str[i + 1 + j]; ++j);//以当前字符为回文中心左侧字符查找最长回文子串
     8         res = max(res, 2 * j);//更新回文子串最大长度
     9     }
    10 }

    使用动态规划

     1 void way3()
     2 {
     3     int dp[1010][1010];
     4     for (int i = 0; i < str.length(); i++)
     5     {
     6         dp[i][i] = 1;//边界
     7         if (i < str.length() - 1 && str[i] == str[i + 1])
     8         {
     9             dp[i][i + 1] = 1;//边界
    10             res = 2;
    11         }
    12     }
    13     for (int L = 3; L <= str.length(); L++) {//因为上面已经初始化了长度L==1和L==2的情形
    14         for (int i = 0; i + L - 1 < str.length(); i++) {
    15             int j = i + L - 1;
    16             if (str[i] == str[j] && dp[i + 1][j - 1] == 1) {
    17                 dp[i][j] = 1;
    18                 res = L;
    19             }
    20         }
    21     }
    22 }
  • 相关阅读:
    我的知识库(4) java获取页面编码(Z)
    知识库(3)JAVA 正则表达式 (超详细)
    The Struts dispatcher cannot be found. This is usually caused by using Struts tags without the associated filter. Struts
    某人总结的《英语听力的技巧 》,挺搞的
    我的知识库(5)java单例模式详解
    构建可扩展程序
    SerialPort (RS232 Serial COM Port) in C# .NET
    Python学习笔记——String、Sequences
    UI题目我的答案
    jQuery学习系列学会操纵Form表单元素(1)
  • 原文地址:https://www.cnblogs.com/zzw1024/p/11924051.html
Copyright © 2020-2023  润新知