目录
1.回文数 ★
#9 判断一个整数是否为回文数
输入:121
输出:true
class Solution {
public boolean isPalindrome(int x) {
String numStr = String.valueOf(x);
int len = numStr.length();
if(len<=3){
return numStr.charAt(0)==numStr.charAt(len-1);
}
for(int i=0;i<=len/2-1;i++){
if(numStr.charAt(i)!=numStr.charAt(len-1-i)){
return false;
}
}
return true;
}
}
2.回文排列 ★
给定一个字符串,编写一个函数判断其能否通过排列成为回文字符串
很简单,只需要判断个数为奇数个的元素是否少于1个
class Solution {
public boolean canPermutePalindrome(String s) {
//奇数个元素计数
int singleCount = 0;
char[] arr = s.toCharArray();
HashMap<Character, Integer> map = new HashMap<>();
for (char c : arr) {
map.put(c, map.getOrDefault(c, 0) + 1);
}
for (Integer i : map.values()) {
if (i % 2 == 1) {
singleCount++;
}
}
return singleCount <= 1;
}
}
3.最长回文子串 ★★★
#5 给定一个字符串,找到s中最长的回文子串,设s的最大长度为1000
1.暴力查找:o(n^3)
for(int i=0;i<len;i++){
for(int j=i;j<len;j++){
if(j-i>maxLen && isHuiWen(arr,i,j)){
maxLen = j-i+1;
begin = i;
}
}
}
动态规划:状态转移 o(N^2)
dp [i][j] 表示s.[i][j] 子串为回文串
- 两层for循环:
- 外层: j : 1~len
- 内层:i : 0~j
- 左右两个字符相等且(内层长1/2或者内层为回文):外层为回文dp[i][j]
- 否则不是,继续遍历子串
- 外层: j : 1~len
理解:实际上还是先用j定位整个字符串位置,然后用i<j判断子串是否回文,在后续的判断中由于dp[][]记录下了之前的子串是否回文,因此将复杂度降低
public String longestPalindrome(String s) {
char[] arr = s.toCharArray();
int len = s.length();
if (len < 2) {
return s;
}
boolean[][] dp = new boolean[len][len];
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
int maxLen = 1;
int begin = 0;
for (int j = 1; j < len; j++) {
for (int i = 0; i < j; i++) {
if (arr[i] == arr[j] && (j - i < 3 || dp[i + 1][j - 1])) {
dp[i][j] = true;
}
if(maxLen < j-i+1 && dp[i][j]){
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
4.分割回文子串 ★★★★
#131 给定一个字符串,将S分割成每个都是回文的子串
返回所有s可能的分割方案
组合问题,回溯解决
class Solution {
List<List<String>> res = new LinkedList<>();
public List<List<String>> partition(String s) {
backtrack(s,0,s.length(),new LinkedList<>());
return res;
}
public void backtrack(String s,int start,int len,LinkedList<String> temp){
if(start==len){
res.add(new LinkedList<>(temp));
return;
}
for(int i=start;i<len;i++){
if(!isHuiWen(s,start,i)){
continue;
}
temp.add(s.substring(start,i+1));
backtrack(s,i+1,len,temp);
temp.remove(temp.size()-1);
}
}
public boolean isHuiWen(String s,int start,int end){
while(start<end){
if(s.charAt(start)!=s.charAt(end)){
return false;
}
start++;
end--;
}
return true;
}
}
每次回溯都要用“两边夹”的方式判断子串是否为回文串,于是想到用空间换时间,先初始化“子串回文矩阵”f[i][j],表示i~j的子串是否会文
class Solution {
List<List<String>> res=new LinkedList<>();
public List<List<String>> partition(String s) {
int len = s.length();
if (len == 0) {
return res;
}
// 初始化“子串回文矩阵”:dp[i][j] 表示 s[i][j] 是否是回文
boolean[][] dp = new boolean[len][len];
for (int right = 0; right < len; right++) {
for (int left = 0; left <= right; left++) {
if (s.charAt(left) == s.charAt(right) && (right - left <= 2 || dp[left + 1][right - 1])) {
dp[left][right] = true;
}
}
}
backtrack(s,0,s.length(),new LinkedList<String>(),dp);
return res;
}
public void backtrack(String s,int start,int len,LinkedList<String> temp,boolean[][] dp){
if(start==len){
res.add(new LinkedList<>(temp));
return;
}
for(int i=start;i<len;i++){
if(!dp[start][i]){
continue;
}
temp.add(s.substring(start,i+1));
backtrack(s,i+1,len,temp,dp);
temp.remove(temp.size()-1);
}
}
}
5.分割回文子串Ⅱ ★★★★
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回符合要求的最少分割次数。
class Solution {
int minCount = Integer.MAX_VALUE;
public int minCut(String s) {
backtrack(s,0,s.length(),new LinkedList<>());
return minCount;
}
public void backtrack(String s,int start,int len,LinkedList<String> temp){
if(start==0){
minCount = Math.min(temp.size(),minCount);
return;
}
for(int i=start;i<len;i++){
if(!isHuiWen(s,start,i)){
continue;
}
temp.add(s.subString(start,i+1));
backtrack(s,i+1,len,temp);
temp.remove(temp.size()-1);
}
}
public boolean isHuiWen(String s,int start,int end){
while(start<end){
if(s.charAt(start)!=s.chaAt(end)){
return false;
}
start++;
end++;
}
return true;
}
}
!!超时,字符串长度越长,深度越大,超时
动态规划
class Solution {
public int minCut(String s) {
int len = s.length();
if (len < 2) {
return 0;
}
//dp[i] 表示0-i子串分割成若干回文子串的最小分割次数
int[] dp = new int[len];
for (int i = 0; i < len; i++) {
dp[i] = i;
}
for (int i = 0; i < len; i++) {
if (isHuiWen(s, 0, i)) {
dp[i] = 0;
continue;
}
for (int j = 0; j < i; j++) {
if (isHuiWen(s, j + 1, i)) {
dp[i] = Math.min(dp[i], dp[j] + 1);
}
}
}
return dp[len - 1];
}
public boolean isHuiWen(String s,int beg,int end){
while(beg<end){
if(s.charAt(beg)!=s.charAt(end)){
return false;
}
beg++;
end--;
}
return true;
}
}
6. 验证回文字符串 ★
给定字符串中含所有可能字符,只考虑子母和数字,判断是否为回文串(可忽视大小写)
输入: "A man, a plan, a canal: Panama" 输出: true
class Solution {
public boolean isPalindrome(String s) {
if (s == null || s.equals(" ")) {
return true;
}
int len = s.length();
int i=0,j=len-1;
while(i<j) {
while (i<j && !Character.isLetterOrDigit(s.charAt(i))){
i++;
}
while (i<j && !Character.isLetterOrDigit(s.charAt(j))){
j--;
}
if (i < j) {
if (Character.toLowerCase(s.charAt(i)) != Character.toLowerCase(s.charAt(j))) {
return false;
}
++i;
--j;
}
}
return true;
}
}
验证回文字符串Ⅱ ★★
给定一个非空字符串S,最多删除一个字符,判断是否能成为回文字符
输入: "abca" 输出: True 解释: 你可以删除c字符。
class Solution {
public boolean validPalindrome(String s) {
int len = s.length();
int beg=0,end=len-1;
while(beg<end){
char c1 = s.charAt(beg);
char c2 = s.charAt(end);
if(c1==c2){
beg++;
end--;
}else{
boolean flag1 = true;
boolean flag2 = true;
for(int i=beg,j=end-1;i<j;i++,j--){
if(s.charAt(i)!=s.charAt(j)){
flag1 = false;
break;
}
}
for(int i=beg+1,j=end;i<j;i++,j--){
if(s.charAt(i)!=s.charAt(j)){
flag2 = false;
break;
}
}
return flag1 || flag2;
}
}
return true;
}
}
7.最长回文串 ★
给定一个包含大小写字母的字符串,找到通过这些字母构造成的最长回文串,区分大小写
输入: "abccccdd" 输出: 7
class Solution {
public int longestPalindrome(String s) {
int[] count = new int[128];
for (char c: s.toCharArray())
count[c]++;
int ans = 0;
for (int v: count) {
ans += v / 2 * 2;
if (v % 2 == 1 && ans % 2 == 0)
ans++;
}
return ans;
}
}
8. 回文子串 ★
计算一个字符串中有多少个回文子串,具有不同开始位置/结束位置的子串,即使是字符相同也认为是不同子串
class Solution {
public int countSubstrings(String s) {
int len = s.length();
int res = 0;
boolean[][] dp = new boolean[len][len];
for (int j = 0; j < len; j++) {
for (int i = 0; i <= j; i++) {
if (s.charAt(i) == s.charAt(j) &&
(j - i <= 2 || dp[i + 1][j - 1])) {
dp[i][j] = true;
res++;
}
}
}
return res;
}
}
9.回文链表
检查输入的链表是否会文
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null) {
return true;
}
Deque<Integer> stack = new LinkedList<>();
Queue<Integer> queue = new LinkedList<>();
int len = 0;
while(head!=null){
stack.addLast(head.val);
queue.offer(head.val);
head = head.next;
len++;
}
len /= 2;
while(len-->0){
if(!stack.removeLast().equals(queue.poll())){
return false;
}
}
return true;
}
}
快慢指针实现翻转前半链表,比较前后部分链表,时间复杂度O(n),空间O(1)
public boolean isPalindrome(ListNode head) {
ListNode slow = head;
ListNode fast= head;
ListNode pre = null;
while(fast!=null && fast.next!=null){
ListNode cur = slow;
slow = slow.next;
fast = fast.next.next;
cur.next = pre;
pre = cur;
}
if(fast != null){
slow = slow.next;
}
while(slow!=null){
if(slow.val!=pre.val){
return fasle;
}
slow = slow.next;
pre = pre.next;
}
return true;
}