今日标题链接:修改间隔
修改间隔
难度:较难
描绘
给定两个字符串 str1 和 str2 ,请你算出将 str1 转为 str2 的最少操作数。
你能够对字符串进行3种操作:
1.刺进一个字符
2.删去一个字符
3.修正一个字符。
举例
解题思路
字符串比较
修改间隔是一类十分经典的动态规划的标题。 咱们运用dp[i][j]表明字符串A的前i个字符与字符串B的前j个字符相同所需求的修改间隔。 首先需求进行状况的初始化,当一个字符串为空时,修改间隔等于另一个字符串的长度 关于状况搬运方程,需求分两种状况讨论: 第一种状况,a[i]==b[j],这种状况下,咱们不需求进行修改,dp[i][j]=dp[i-1][j-1] 第二种状况,a[i]!=b[j],假如两个字符不持平,咱们有三种处理方式:替换字符串b,修改间隔为dp[i-1][j-1]+1;刺进一个字符与其持平,则修改间隔为dp[i-1][j]+1;删去该字符,修改间隔为dp[i][j-1]+1,三者取其小即可。 具体以下图为例
完成代码(java)
import java.util.*;
public class Solution {
/**
* 代码中的类名、办法名、参数名现已指定,请勿修正,直接回来办法规则的值即可
*
*
* @param str1 string字符串
* @param str2 string字符串
* @return int整型
*/
public int editDistance (String str1, String str2) {
// write code here
int[][] dp = new int[str1.length()+1][str2.length()+1]; //界说动规数组
for(int i=1; i<=str1.length(); i++){ // 初始化
dp[i][0] = i;
}
for(int i=1; i<=str2.length(); i++){ // 初始化
dp[0][i] = i;
}
for(int i=1; i<=str1.length(); i++){
for(int j=1; j<=str2.length(); j++){
if(str1.charAt(i-1)==str2.charAt(j-1)){ //第一种状况
dp[i][j] = dp[i-1][j-1];
}else{ //第二种状况
dp[i][j] = Math.min(dp[i-1][j]+1, Math.min(dp[i-1][j-1]+1, dp[i][j-1]+1)); //状况搬运方程
}
}
}
return dp[str1.length()][str2.length()];
}
}
学习完本题的思路你能够解决如下标题:
最长的括号子串
难度:较难
描绘
给出一个长度为 n的,仅包括字符 ‘(‘ 和 ‘)’ 的字符串,核算最长的格局正确的括号子串的长度。
例1: 关于字符串 “(()” 来说,最长的格局正确的子串是 “()” ,长度为 2 .
例2:关于字符串 “)()())” , 来说, 最长的格局正确的子串是 “()()” ,长度为 4 .
举例
解题思路
办法一:双指针;
- 新建两个变量left和right,别离记载左右括号出现次数。
- 正向遍历一次字符串,假如左右括号持平,则更新格局正确的括号子串长度,取较大者。假如左括号数小于右括号数,阐明有不合法右括号(前面没有左括号与之匹配),重置为0。
- 最后反向遍历一次字符串,假如左右括号持平,则更新格局正确的括号子串长度,取较大者。假如左括号数大于右括号数,阐明有不合法左括号(后面没有右括号与之匹配),重置为0。
办法二:栈; 由于括号需求一一匹配,而且先来的左括号,只能匹配后面的右括号,因而能够考虑运用栈的先进后出功用,使括号匹配。
具体做法:
- step 1:能够运用栈来记载左括号下标。
- step 2:遍历字符串,左括号入栈,每次遇到右括号则弹出左括号的下标。
- step 3:然后长度则更新为当前下标与栈顶下标的间隔。
- step 4:遇到不符合的括号,或许会使栈为空,因而需求运用start记载上一次完毕的方位,这样用当前下标减去start即可获取长度,即得到子串。
- step 5:循环中最后保护子串长度最大值。
完成代码(java)
办法一:
import java.util.*;
public class Solution {
/**
*
* @param s string字符串
* @return int整型
*/
public int longestValidParentheses (String s) {
// write code here
int max=0;
//别离记载左右括号出现次数
int left=0,right=0;
//正向遍历
for(int i=0;i<s.length();i++){
if(s.charAt(i)=='('){
left++;
}
else{
right++;
}
//假如左右括号持平,则更新格局正确的括号子串长度,取较大者
if(left==right){
max=Math.max(max,right*2);
}
//假如左括号数小于右括号数,阐明有不合法右括号,重置为0
else if(left<right){
left=right=0;
}
}
left=right=0;
//逆向遍历
for(int i=s.length()-1;i>=0;i--){
if(s.charAt(i)=='('){
left++;
}
else{
right++;
}
//假如左右括号持平,则更新格局正确的括号子串长度,取较大者
if(left==right){
max=Math.max(max,left*2);
}
//假如左括号数大于右括号数,阐明有不合法左括号,重置为0
else if(left>right){
left=right=0;
}
}
return max;
}
}
办法二:
import java.util.*;
public class Solution {
public int longestValidParentheses (String s) {
int res = 0;
//记载上一次连续括号完毕的方位
int start = -1;
Stack<Integer> st = new Stack<Integer>();
for(int i = 0; i < s.length(); i++){
//左括号入栈
if(s.charAt(i) == '(')
st.push(i);
//右括号
else{
//假如右括号时栈为空,不合法,设置为完毕方位
if(st.isEmpty())
start = i;
else{
//弹出左括号
st.pop();
//栈中还有左括号,阐明右括号不行,减去栈顶方位就是长度
if(!st.empty())
res = Math.max(res, i - st.peek());
//栈中没有括号,阐明左右括号行号,减去上一次完毕的方位就是长度
else
res = Math.max(res, i - start);
}
}
}
return res;
}
}
学习完本题的思路你能够解决如下标题: BM73.最长回文子串
最长回文子串
难度:中等
描绘
关于长度为n的一个字符串A(仅包括数字,大小写英文字母),请设计一个高效算法,核算其中最长回文子串的长度。
举例
解题思路
办法一:中心分散;
-
每个字符都能够测验作为中心点看,会出现两种状况:或许是相似 aba 的字符串,也或许是相似 abba 的状况
-
只需求别离核算出以一个和两个字符作为中心点的子串,取出较大的长度即可
- 从left到right开端向两头分散、比较,假如持平则持续分散比较
- 假如不持平则剪枝,不必再持续分散比较
-
核算每次比较的回文子串长度,取最大
办法二:动态规划;保护一个布尔型的二维数组dp,dp[i][j]
表明 i 到 j 的子串是否是回文子串
每次先判别鸿沟字符是否持平,再取决于上个状况的判别成果
算法流程:
-
保护一个布尔型的二维数组dp,
dp[i][j]
表明 i 到 j 的子串是否是回文子串 -
从长度0到字符串长度n进行判别
-
选定起始下标 i 和停止下标 j, i 和 j 别离为要比较的字符串的左右鸿沟指针
-
从左右鸿沟字符开端判别,即 A.charAt(i) == A.charAt(j)
- 当持平时,还要判别当前长度 c 是否大于1,不大于则表明只要两个字符的字符串,一个或两个字符肯定是回文串,如“11”
- 判别的长度大于1时,由于最左右的字符现已持平,因而取决于上一次的子串是否是回文子串, 如 “12121”
-
更新回文串的最大长度
完成代码(java)
办法一:
import java.util.*;
public class Solution {
public int getLongestPalindrome(String A, int n) {
if(n < 2) {
return n;
}
// 最大长度
int res = 0;
// 每个字符都能够测验作为中心点
for(int i = 0; i < n; i++) {
// 两种状况:或许是相似 aba 的字符串,也或许是相似 abba 的状况
// 只需求别离核算出以一个和两个字符作为中心点的子串,取出较大的长度即可
int len = helper(A, i, i) > helper(A, i, i+1) ? helper(A, i, i) : helper(A, i, i + 1);
// 更新最大长度
res = Math.max(res, len);
}
return res;
}
public int helper(String A, int left, int right) {
// 从left到right开端向两头分散、比较
while(left >= 0 && right < A.length()) {
// 假如持平则持续分散比较
if(A.charAt(left) == A.charAt(right)) {
left--;
right++;
continue;
}
// 假如不持平则剪枝,不必再持续分散比较
break;
}
// "+1"是由于通过下标核算子串长度
// "-2"是由于上边的while循环是当索引为left和right不想等才退出循环的
// 因而此时的left和right是不满足的,需求舍弃
return right - left + 1 - 2;
}
}
办法二:
import java.util.*;
public class Solution {
public int getLongestPalindrome(String A, int n) {
// 动态规划:i到j的子串是否是回文子串
boolean[][] dp = new boolean[n][n];
int max = 0;
// 字符串长度差 c = j-i,即当前要比较的字符串长度,这里能够 c <= n / 2 + 1,减少判别次数
for(int c = 0; c <= n + 1; c++) {
// 起始下标,规模取决于要判别的字符串长度c
// i 和 j 别离为要比较的字符串的左右鸿沟指针
for(int i = 0; i < n - c; i++) {
// 终点下标
int j = c + i;
// 左右鸿沟的字符持平
if(A.charAt(i) == A.charAt(j)) {
// c <= 1表明只要两个字符的字符串,一个或两个字符肯定是回文串
if(c <= 1) {
dp[i][j] = true;
} else {
// 关于两个字符以上的字符串
// 由于最左右的字符现已持平,因而取决于内层的子串是否是回文子串
dp[i][j] = dp[i + 1][j - 1];
}
// 更新回文串的最大长度,c代表判别的子串长度,越来越大
if(dp[i][j]) {
max = c + 1;
}
}
}
}
return max;
}
}