回文串
“回文串”是一个从左读和从右读都一样的字符串,比如“level”或者“noon”等等就是回文串。
常见的回文串有:dad,123454321,123456654321….
题目1:判断一个字符串是否为回文串
递归法:
public static boolean check(String str) {
if ( str.charAt(0) != str.charAt(str.length() - 1) ) {
return false;
}
if (str.length() <= 1) {
return true;
}
//可改成if...else if....else
return check(str.substring(1, str.length() - 1));//切割两端,将子串递归调用
}
循环法:
public boolean isPalindromeNumber(long num){
String str = num + "";
int start = 0;
int end = str.length() - 1;
while(start <= end){
char s = str.charAt(start);
char e = str.charAt(end);
if(s != e){
return false;
}else{
start++;
end--;
}
}
return true;
}
题目2:给定一个字符串s,你可以从中删除一些字符,使得剩下的串是一个回文串。如何删除才能使得回文串最长呢?
输出需要删除的字符个数。
解题思路:动态规划 :分解成小问题,并且使用额外的辅助空间来完成最最优解的保存。
提到回文串,自然要利用回文串的特点,想到将源字符串逆转后,“回文串”(不一定连续)相当于顺序没变
求原字符串和其反串的最大公共子序列(不是子串,因为可以不连续)的长度(使用动态规划很容易求得),
然后用原字符串的长度减去这个最大公共子串的长度就得到了最小编辑长度。
public class PalindromicSubstring {
public static void main(String[] args) {
// System.out.println("请输入");
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String s1 = sc.next();
String s2 = new StringBuilder(s1).reverse().toString();
int[][] dp = new int[s1.length() + 1][s2.length() + 1];
for (int i = 1; i < dp.length; i++) {
for (int j = 1; j < dp[0].length; j++) {
dp[i][j] = s1.charAt(i - 1) == s2.charAt(j - 1) ? dp[i - 1][j - 1] + 1
: Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
System.out.println(s1.length() - dp[s1.length()][s2.length()]);
}
}
}
G | A | A | T | T | C | A | G | T | T | A | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
G | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
G | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 |
A | 0 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
T | 0 | 1 | 2 | 2 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
C | 0 | 1 | 2 | 2 | 3 | 3 | 4 | 4 | 4 | 4 | 4 | 4 |
G | 0 | 1 | 2 | 2 | 3 | 3 | 3 | 4 | 5 | 5 | 5 | 5 |
A | 0 | 1 | 2 | 3 | 3 | 3 | 3 | 4 | 5 | 5 | 5 | 6 |
参考文章 http://www.cnblogs.com/grenet/archive/2010/06/03/1750454.html
若是要求连续:求两个字符串的最长公共连续子串长度。代码如下:
import java.util.Scanner;
public class LongestComSubString {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String s1 = in.next();
String s2 = in.next();
char[] c1 = s1.toCharArray();
char[] c2 = s2.toCharArray();
int max = 0;
int[][] matrix = new int[c1.length + 1][c2.length + 1]; // 定义2维数组
for (int i = 1; i < matrix.length; i++) {
for (int j = 1; j < matrix[i].length; j++) {
if (c1[i - 1] == c2[j - 1]) { // 横向字符与纵向字符相同的时候
matrix[i][j] = matrix[i - 1][j - 1] + 1; // 该位置的值等于上个对角位置的值加1
if (matrix[i][j] > max) {// 跟踪最大值
max = matrix[i][j];
}
}
}
}
System.out.println(max);
}
}
由上可知:若可以不连续:
dp[i][j] = s1.charAt(i - 1) == s2.charAt(j - 1) ? dp[i - 1][j - 1] + 1: Math.max(dp[i - 1][j], dp[i][j - 1]);
若要求连续:
matrix[i][j] = matrix[i - 1][j - 1] + 1;
法二:
对于一个字符串,请设计一个高效算法,计算其中最长连续回文子串的长度。给定字符串A以及它的长度n,请返回最长回文子串的长度。
思路:
本题使用暴力破解法,时间复杂度为O(N的立方),我们先看一种使用动态规划,使得时间复杂度降为了O(N的平方)的算法。
动态规划:分解成小问题,并且使用额外的辅助空间来完成最最优解的保存。
LeetCode原题:需要的不是最长的回文串长度,而是让你输出最长的回文串(连续),则我们在更新max变量的时候,必须一并将其pair数组的两个下标保存,最后根据start和end变量来截取原字符串,输出最长回文子串
题目3:给你一个字符串,可在任意位置添加字符,最少再添加几个字符,可以使这个字符串成为回文字符串。
解题思路:
同上题,插入数与删除数是一样的,都是原字符串长度-与逆序串的最长公共子序列长度。
该算法的核心是求最长公共子序列,最长公共子序列有DP的求法,算法的复杂度是 时间和空间都是 O(n^2)
若已知原字符串与最长公共子序列,若要求最后的回文串,如何添加字符?
如:原字符串:AB1EF2GHI1CD
最长公共子序列:121
添加步骤:以最长公共子序列为“皮”,一层一层往下剥。
左边:左顺序+右逆序;右边:右顺序+左逆序