删除部分字符使其变成回文串问题——最长公共子序列LCS问题

先要搞明白:最长公共子串和最长公共子序列的区别。java

最长公共子串(Longest Common Substirng):连续ui

最长公共子序列(Longest Common Subsequence,LCS):没必要连续code

题目:给定一个字符串s,你能够从中删除一些字符,使得剩下的串是一个回文串。如何删除才能使得回文串最长呢?
输出须要删除的字符个数。
字符串

思路是:反序这个字符串,求这个新串和原串的最大子序列。abcda--->adcba 最大子序列是aca,再相减就是最少的字符删除个数。因此问题变成了求两个字符串的最长子序列。get

那么怎么求两个字符串的最长子序列呢?既然是用动态规划,最重要的是肯定状态和状态转移方程。io

状态:当str1的下标为m,str2的下标是n的时候(不考虑后面的),此时的最长子序列长度L。class

转移方程:1,若是str1(m)==str2(n),那么L(m,n)=L(m-1,n-1)+1。2,若是str1(m)!=str2(n),那么L(m,n)=max(L(m-1,n),L(m,n-1))。next

解释一下,假如m和n相等,那么这个时候最长子序列无疑是前一个L(m-1,n-1)加上1,由于这两个字符串这个地方的字符均可以加入到最长子序列里面去。若是不相等,那么要么舍弃新来的来自str1的那个字符m号,要么舍弃str2的n号字符(最长子序列每一个位置上固然都是惟一肯定的一个字符),舍弃以后呢,就从static

L(m-1,n),L(m,n-1)当中挑一个好的(能更长的)为当前状态的最长子序列。动态规划

代码:

public class Main {
     public static void main(String[] args){
         Solution s = new Solution();
         Scanner sc = new Scanner(System.in);
         while(sc.hasNextLine()) {
             System.out.println( s.getResult(sc.nextLine()) );
         }
         sc.close();
     }
}
 
class Solution {
     public int getResult(String s) {
         StringBuilder s1 = new StringBuilder(s);
         StringBuilder s2 = new StringBuilder(s).reverse();
         return s.length() - LCS(s1, s2);
     }
     public int LCS(StringBuilder s1, StringBuilder s2) {
         int m = s1.length();
         int n = s2.length();
         int[][] mutrix = new int[m + 1][n + 1];
         
         for(int i = 1; i <= m; i++) {
             for(int j = 1; j <= n; j++) {
                 if(s1.charAt(i - 1) == s2.charAt(j - 1))
                     mutrix[i][j] = mutrix[i - 1][j - 1] + 1;
                 else 
                     mutrix[i][j] = Math.max(mutrix[i - 1][j], mutrix[i][j - 1]);
             }
         }
         return mutrix[m][n];
     }
}
相关文章
相关标签/搜索