超全的回文题汇总-Palindrome

最近做了好多回文类题目,一连串发现了好多,做了如下汇总:

1.判断单链表是否是回文 - Palindrome Linked List

 Time Complexity: O(n), Space Complexity: O(n)
    使用快慢指针,每次快指针走两步,慢指针走一步。同时还要用栈,每次慢指针走一步,都把值存入栈中。等快指针走完时,链表的前半段都存入栈中了。最后慢指针继续往前走,每次与栈顶元素进行比较。

public static boolean isPalindrome(ListNode head) {
		if (head == null) {
			return true;
		}

		int lenth = 0;// 记录长度
		ListNode slow = head;
		ListNode fast = head.next;
		Stack<ListNode> s = new Stack<>();
		while (fast != null && fast.next != null) {// 找到中间节点
			s.push(slow);
			slow = slow.next;
			fast = fast.next.next;
			lenth++;
		}

		fast = slow;
		while (fast != null) {// 计算长度
			fast = fast.next;
			lenth++;
		}

		slow = slow.next;
		if (lenth % 2 == 0) {// 如果为奇数,丢弃中间值
			s.push(slow);
		}

		while (slow != null) {
			if (slow.val != s.pop().val) {
				return false;
			}
			slow = slow.next;
		}
		return true;
	}

 2.判断字符串是否是回文 -  Valid Palindrome

  时间复杂度O(n),空间复杂度O(1)
 左右夹逼法 

public static boolean isPalindrome(String s) {
		s = s.toLowerCase();
		int left = 0;
		int right = s.length() - 1;
		while (left < right) {
			if (!Character.isLetterOrDigit(s.charAt(left)))
				++left;
			else if (!Character.isLetterOrDigit(s.charAt(right)))
				--right;
			else if (s.charAt(left) != s.charAt(right))
				return false;
			else {
				++left;
				--right;
			}
		}
		return true;
	}

3. 判断数组是否是回文 - Palindrome Number 

 // 时间复杂度O(1),空间复杂度O(1)
 // 不断地取第一位和最后一位(10进制下)进行比较,相等则取第二位和倒数第二位,直到完成比较或者中途找到了不一致的位。

public static boolean isPalindrome(int x) {
		if (x < 0)
			return false;
		int d = 1; // divisor
		while (x / d >= 10)
			d *= 10;
		while (x > 0) {
			int q = x / d; // quotient
			int r = x % 10; // remainder
			if (q != r)
				return false;
			x = x % d / 10;
			d /= 100;
		}
		return true;
	}

4.求最长子回文 - Longest Palindromic Substring

方法一:  

动规,时间复杂度O(n^2),空间复杂度O(n^2)
设状态为 f(i,j) ,表示区间[i,j]是否为回文串

public static String longestPalindrome(String s) {
		final int n = s.length();
		final boolean[][] f = new boolean[n][n];
		int maxLen = 1, start = 0; // 最长回文子串的长度,起点
		for (int i = n - 1; i >= 0; i--) {
			f[i][i] = true;
			for (int j = i; j < n; j++) { // [j, i]
				f[i][j] = (s.charAt(i) == s.charAt(j) && (j - i < 2 || f[i + 1][j - 1]));
				if (f[i][j] && maxLen < (j - i + 1)) {
					maxLen = j - i + 1;
					start = i;
				}
			}
		}
		return s.substring(start, start + maxLen);
	}

方法二: 

Manacher,时间复杂度O(n),空间复杂度O(n)

public static String Manacher(String s) {
		int len = s.length();
		// 转换原始串
		char[] tmp = new char[2 * len + 1];
		for (int i = 0; i < 2 * len; i += 2) {
			tmp[i] = '#';
			tmp[i + 1] = s.charAt(i / 2);
		}
		tmp[2 * len] = '#';

		// Manacher算法计算过程
		int[] Len = new int[2 * len + 1];
		int index = -1;
		int mx = 0, ans = 0, po = 0;// mx即为当前计算回文串最右边字符的最大值
		for (int i = 0; i < 2 * len + 1; i++) {
			if (mx > i)
				Len[i] = Math.min(mx - i, Len[2 * po - i]);// 在Len[j]和mx-i中取个小
			else
				Len[i] = 1;// 如果i>=mx,要从头开始匹配
			while (i - Len[i] >= 0 && i + Len[i] < 2 * len + 1 && tmp[i - Len[i]] == tmp[i + Len[i]])
				Len[i]++;
			if (Len[i] + i > mx)// 若新计算的回文串右端点位置大于mx,要更新po和mx的值
			{
				mx = Len[i] + i;
				po = i;
			}
			if (ans < Len[i]) {
				ans = Len[i];
				index = i / 2;
			}
		}
		int n = ans - 1;
		String ret = "";
		if (n % 2 == 0) {
			ret = s.substring(index - n / 2, index) + s.substring(index, index + n / 2);
		} else {
			ret = s.substring(index - n / 2, index) + s.substring(index, index + n / 2 + 1);
		}

		return ret;// 返回Len[i]中的最大值-1即为原串的最长回文子串额长度
	}

5.切分字符串,求所有切分的回文串 - Palindrome Partitioning

 // 深搜,时间复杂度O(2^n),空间复杂度O(n)
 // 在每一步都可以判断中间结果是否为合法结果,用回溯法。

public static List<List<String>> partition(String s) {
		List<List<String>> result = new ArrayList<>();
		List<String> path = new ArrayList<>(); // 一个partition方案
		dfs(s, path, result, 0);
		return result;
	}

	// 搜索必须以s[start]开头的partition方案
	private static void dfs(String s, List<String> path, List<List<String>> result, int start) {
		if (start == s.length()) {
			result.add(new ArrayList<>(path));
			return;
		}
		for (int i = start; i < s.length(); i++) {
			if (isPalindrome(s, start, i)) { // 从i位置砍一刀
				path.add(s.substring(start, i + 1));
				dfs(s, path, result, i + 1); // 继续往下砍
				path.remove(path.size() - 1); // 撤销上上行
			}
		}
	}

	private static boolean isPalindrome(String s, int start, int end) {
		while (start < end && s.charAt(start) == s.charAt(end)) {
			++start;
			--end;
		}
		return start >= end;
	}

6. 切分字符串,求最小切分数 - Palindrome Partitioning


   // 动规,时间复杂度O(n^2),空间复杂度O(n^2)

   // f(i)=区间[i, n-1]之间最小的cut数 ,n为字符串长度,

public static int minCut(String s) {
		final int n = s.length();
		int[] f = new int[n + 1];
		// f[n] = Integer.MAX_VALUE;
		boolean[][] p = new boolean[n][n];
		// the worst case is cutting by each char
		for (int i = n - 1; i >= 0; i--) {
			p[i][i] = true;
			f[i] = f[i + 1] + 1;
			for (int j = i; j < n; j++) {
				if (s.charAt(i) == s.charAt(j) && (j - i < 2 || p[i + 1][j - 1])) {
					p[i][j] = true;
					f[i] = Math.min(f[i], f[j + 1] + 1);
				}
			}
			// System.out.println(f[i]);
		}
		return f[0] - 1;
	}

7.数组中插入数变成回文,使插入数的和最小 - PalindromeArray

//动规, dp[i][j],i到j的回文数组的和

public static void palindromeArray(int[] nums) {
		int len = nums.length;
		Integer[][] dp = new Integer[len][len];
		int res = palindromeArrayHelper(nums, dp, 0, len - 1);
		System.out.println(res);
	}

	public static int palindromeArrayHelper(int[] nums, Integer[][] dp, int i, int j) {
		if (i > j) {
			return 0;
		}
		if (i == j) {
			return nums[i];
		}
		if (dp[i][j] != null) {
			return dp[i][j];
		}
		if (nums[i] == nums[j]) {
			dp[i][j] = 2 * nums[i] + palindromeArrayHelper(nums, dp, i + 1, j - 1);
		} else {
			dp[i][j] = Math.min(2 * nums[i] + palindromeArrayHelper(nums, dp, i + 1, j),
					2 * nums[j] + palindromeArrayHelper(nums, dp, i, j - 1));
		}

		return dp[i][j];
	}

 8.只能在字符串末尾插入字符,使成为回文,输出末尾插入的字符串

 // 类似于Longest Palindromic Substring,只能在末尾填加
 // 动规,时间复杂度O(n^2),空间复杂度O(n^2)
 // 设状态为 f(i,j) ,表示区间[i,j]是否为回文串

public static String Lastpalindrome(String s) {
		final int n = s.length();
		final boolean[][] f = new boolean[n][n];
		int maxLen = 1, start = 0; // 最长回文子串的长度,起点
		for (int i = n - 1; i >= 0; i--) {
			f[i][i] = true;
			for (int j = i; j < n; j++) {
				f[i][j] = (s.charAt(i) == s.charAt(j) && (j - i < 2 || f[i + 1][j - 1]));
				if (j == n - 1 && f[i][j] && maxLen < j - 1 + 1) {
					maxLen = j - i + 1;
					start = i;
				}
			}
		}
		return new StringBuffer(s.substring(0, n - maxLen)).reverse().toString();
	}

猜你喜欢

转载自blog.csdn.net/qq_19446965/article/details/81513591