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

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

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

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

实在是汗颜,网上做一道题半天没进展:

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

首先是自己大致上能明白应该用动态规划的思想否则算法复杂度必然过大。可是对于回文串很难找到其状态和状态转移方程,换句话说就是:某个序列是不是回文串和这个序列的子序列没有明显关系,一个序列是回文串再加上一个字母可能就不是了。所以其状态很难找。

最近没有太多时间,不能在一个问题上闭门造车(后来来看其实也对,这种问题是计算机系的课堂例题,单纯自己苦想比较难想到)。

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

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

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

转移方程: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))

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

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

代码:

 1 import java.util.Scanner;
 2
 3 public class Main {
 4     public static void main(String[] args) {
 5         Solution s = new Solution();
 6         Scanner sc = new Scanner(System.in);
 7         while(sc.hasNextLine()) {
 8             System.out.println( s.getResult(sc.nextLine()) );
 9         }
10         sc.close();
11     }
12 }
13
14 class Solution {
15     public int getResult(String s) {
16         StringBuilder s1 = new StringBuilder(s);
17         StringBuilder s2 = new StringBuilder(s).reverse();
18         return s.length() - LCS(s1, s2);
19     }
20     public int LCS(StringBuilder s1, StringBuilder s2) {
21         int m = s1.length();
22         int n = s2.length();
23         int[][] mutrix = new int[m + 1][n + 1];
24
25         for(int i = 1; i <= m; i++) {
26             for(int j = 1; j <= n; j++) {
27                 if(s1.charAt(i - 1) == s2.charAt(j - 1))
28                     mutrix[i][j] = mutrix[i - 1][j - 1] + 1;
29                 else
30                     mutrix[i][j] = Math.max(mutrix[i - 1][j], mutrix[i][j - 1]);
31             }
32         }
33         return mutrix[m][n];
34     }
35 }

        

时间: 2024-10-17 23:58:47

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

回文串(最长公共子序列)

一个字符串如果从左往右读和从右往左读都一样,那么这个字符串是一个回文串.例如:"abcba","abccba". 蒜头君想通过添加字符把一个非回文字符串变成回文串.例如:"trit",可以添加一个i变成回文串"tirit". 请你用程序计算出,对于一个给定的字符串,最少需要添加几个字符,才能变成回文串. 输入格式 输入一个长度为n(1≤n≤3000)的字符串.(字符串只包含字母) 输出格式 输出最少需要添加的字符个数,占一行.

添加字符判断是否为回文串

题目:给定一个字符串,问是否能通过添加一个字母将其变为回文串. 提要:所有代码皆为C++语言. 看到题,首先想到如何判断回文串 注:回文串添加或删除指定字符一定还是回文串. 对于一个字符串,从左右两端开始,以此判断,直到读取超出字符串中间. 代码如下: #include<iostream> #include<string.h> using namespace std; bool JudgeReverseString(char *str) { //对比对应位置字符是否相同 int l

集训第五周动态规划 G题 回文串

Description A palindrome is a symmetrical string, that is, a string read identically from left to right as well as from right to left. You are to write a program which, given a string, determines the minimal number of characters to be inserted into t

409.求最长回文串的长度 LongestPalindrome

题目要求求出长度即可,并不需要求出最长回文串. 思路:用字典统计每一个字符的出现次数,出现次数大于1的字符必定出现在回文串中,另外还再加上一个中心点. public static int LongestPalindrome(string s) { int length = 0; Dictionary<char, int> dictionary = new Dictionary<char, int>(); int value = 0; foreach (char c in s) {

poj 1159 Palindrome -- 回文串,动态规划

Palindrome Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 59029   Accepted: 20505 Description A palindrome is a symmetrical string, that is, a string read identically from left to right as well as from right to left. You are to write a

最长(大)回文串的查找(字符串中找出最长的回文串)PHP实现

首先还是先解释一下什么是回文串:就是从左到右或者从右到左读,都是同样的字符串.比如:上海自来水来自海上,bob等等. 那么什么又是找出最长回文串呢? 例如:字符串abcdefedcfggggggfc,其中efe,defed,cdefedc,gg,ggg,gggg,ggggg,gggggg,fggggggf,cfggggggfc都是回文串,左右完全一样. 这其中,有最短的gg,最长的cfggggggfc,还有其他长度的.忽略长度为1的.毕竟一个字符的都算回文了. 那么,找出最长的,就是找出这个cf

(Manacher Algorithm, 中心拓展法,动态规划) leetcode 5. 最长回文串

解法一:中心拓展法.从下标为0开始遍历,将每个元素当作回文串中心,向两边拓展. 1)以这个字符为中心的回文串的长度(奇数串): 2)以这个字符和下个字符为中心的回文串的长度(偶数串). 注意:既要统计回文串为奇数时,又要统计回文串为偶数时.当 s[left]!=s[right] 时,left多减了1,right多加了1,所以在计算回文串开头时要把left+1,长度要是(right-1)-(left+1)-1 = right - left -1 class Solution { public: s

【BZOJ】【2565】最长双回文串

Manacher算法 找出一个最长子串S=X+Y,且X和Y都是回文串,求最长的长度是多少…… 同时找两个串明显很难搞啊……但是我们可以先找到所有的回文串!在找回文串的同时我们可以预处理出来l[i]和r[i]分别表示从 i 这个位置开始向左/右最长的回文串有多长,那么我们枚举两个回文串的分割点更新答案即可. 1 /************************************************************** 2 Problem: 2565 3 User: Tunix

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

思路:回文串的特点是,逆序输出和正序输出是一样的.所以这道题可以从这方面来考虑.如果将此字符串逆序输出,那么两个字符串的最长公共子序列将是最长的回文字符串,那么剩余的值将是要删除的字符个数. 求LCS的方法可以在csdn中找到. import java.util.*; public class Main{ public static void main(String[] args){ Scanner scan = new Scanner(System.in); while(scan.hasNex