子串、子序列问题
# 子串、子序列问题
字符串
最长公共子序列
dp[i] [j] 表示以下标i结尾的str1 和 以下标j结尾的str2的最长公共子序列的长度。
dp[i] [j]=
- 0, i=0或者j=0
- dp[i-1] [j-1]+1, str[i-1]==str[j-1]
- max( dp[i-1] [j], dp[i] [j-1] ), str[i-1]!=str[j-1]
public int longestCommonSubsequence(String text1, String text2) {
if (text1 == null || text2 == null) {
return 0;
}
int len1 = text1.length();
int len2 = text2.length() ;
int[][] dp = new int[len1+1][len2+1];
for (int i = 1; i <=len1; i++) {
for (int j = 1; j <=len2; j++) {
if(text1.charAt(i-1)==text2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1]+1;
}else {
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
}
}
}
return dp[len1][len2];
}
最长公共子串
dp[i] [j] 表示以下标i结尾的str1 和 以下标j结尾的str2的最长公共子串的长度。
dp[i] [j]=
- 0, i=0或者j=0
- dp[i-1] [j-1]+1, str1[i-1]==str2[j-1]
- 0, str1[i-1]!=str2[j-1]
public int longestCommonSubStr(String text1, String text2) {
if (text1 == null || text2 == null) {
return 0;
}
int res = 0;
int len1 = text1.length();
int len2 = text2.length();
int[][] dp = new int[len1 + 1][len2 + 1];
for (int i = 1; i <= len1; i++) {
for (int j = 1; j <= len2; j++) {
if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
}
res = Math.max(dp[i][j], res);
}
}
return res;
}
最长不含重复字符的子字符串
滑动窗口:
- 刚开始right向右移动,同时更新max。直到出现了重复字符a,right停止移动,然后left向右移动,滑动窗口减小
- 当窗口中没有重复字符a时,left停止移动,更新max。
- 重复上述过程,直到字符串结尾。
public int lengthOfLongestSubstring_1(String s) {
if (s.length() == 0 || s == null) {
return 0;
}
char[] chars = s.toCharArray();
Map<Character, Integer> windows = new HashMap<>();
int left = 0;
int right = 0;
int res = 0;
//每次移动right,增大滑动窗口时更新res
for (int i = 0; i < chars.length; i++) {
windows.put(chars[i], windows.getOrDefault(chars[i], 0) + 1);
right++;
while (windows.get(chars[i]) > 1) {
char c = chars[left];
windows.put(c, windows.get(c) - 1);
left++;
}
res = Math.max(res, right - left);
}
return res;
}
最长回文子序列
dp[i] [j] 表示从i到j的最长回文子序列的长度
状态转移方程
dp[i] [j] = dp[i+1] [j-1] + 2 , s[i] == s[j]
dp[i] [j] = max(dp[i] [j-1] , dp[i+1] [j]) ,s[i] != s[j]
// 动态规划,状态转移方程
public int longestPalindromeSubseq(String s) {
if (s == null || s.length() == 0) {
return 0;
}
int[][] dp = new int[s.length()][s.length()];
// 初始化
for (int i = 0; i < dp.length; i++) {
dp[i][i] = 1;
}
// 状态转移方程
for (int i = dp.length - 2; i >= 0; i--) {
for (int j = i + 1; j < dp[i].length; j++) {
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
}
}
}
return dp[0][s.length() - 1];
}
最长回文子串
从中间向两边扩散来判断回文串,需要考虑字符串是奇数还是偶数,因为两者的中心不同。
// 中心扩散法,时间复杂度O(n^2)
public String longestPalindrome(String s) {
if (s == null || s.length() == 0) {
return s;
}
String res = "";
for (int i = 0; i < s.length(); i++) {
String s1 = expandCenter(s, i, i);
String s2 = expandCenter(s, i, i + 1);
res = s1.length() > res.length() ? s1 : res;
res = s2.length() > res.length() ? s2 : res;
}
return res;
}
public String expandCenter(String s, int left, int right) {
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
left--;
right++;
}
return s.substring(left + 1, right);
}
数组
动态规划
最长递增子序列
dp[i]表示以i结尾的数组的最长递增子序列的长度
dp[i]=
- max( dp[j]+1 ) , nums[i]>nums[j] , 0<=j<i
- 1 , nums[i]<=nums[j]
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums.length==0||nums==null){
return 0;
}
int n=nums.length;
int[] dp=new int[n];
Arrays.fill(dp,1);
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
dp[i]=Math.max(dp[j]+1,dp[i]);
}
}
}
int maxLen=0;
for(int i=0;i<n;i++){
maxLen=Math.max(maxLen,dp[i]);
}
return maxLen;
}
}
最长递增子序列的个数
count[i] 表示以i结尾的数组最长递增子序列的个数
count[i] =
当nums[i] > nums[j] , 0<=j<i时
dp[j]+1==dp[i], count[i]=count[j]+count[i]
dp[j]+1>dp[i], count[i] = count[j]
class Solution {
public int findNumberOfLIS(int[] nums) {
if(nums.length==0||nums==null){
return 0;
}
int n=nums.length;
int[] dp=new int[n];
int[] count=new int[n];
Arrays.fill(dp,1);
Arrays.fill(count,1);
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
if(dp[j]+1==dp[i]){
count[i]+=count[j];
}
if(dp[j]+1>dp[i]){
count[i]=count[j];
}
dp[i]=Math.max(dp[j]+1,dp[i]);
}
}
}
int maxLen=0;
for(int i=0;i<n;i++){
maxLen=Math.max(maxLen,dp[i]);
}
return maxLen;
}
}
最长递增子串
dp[i] 表示以i结尾的数组最长递增子串的长度
dp[i]=
- dp[i-1] +1 , nums[i]>nums[i-1], 1<=i<n
- 1 , nums[i]<=nums[i-1], 1<=i<n
public int findLengthOfLCIS(int[] nums) {
if (nums.length == 0 || nums == null) {
return 0;
}
if (nums.length == 1) {
return 1;
}
int n = nums.length;
int[] dp = new int[n];
Arrays.fill(dp, 1);
int res = 0;
for (int i = 1; i < n; i++) {
if (nums[i] > nums[i - 1]) {
dp[i] = dp[i - 1] + 1;
}
res = Math.max(res, dp[i]);
}
return res;
}
Talk is cheap,show me your code.