编辑距离
72. 编辑距离
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
示例 1:
输入:word1 = “horse”, word2 = “ros”
输出:3
解释:
horse -> rorse (将 ‘h’ 替换为 ‘r’)
rorse -> rose (删除 ‘r’)
rose -> ros (删除 ‘e’)
示例 2:
输入:word1 = “intention”, word2 = “execution”
输出:5
解释:
intention -> inention (删除 ‘t’)
inention -> enention (将 ‘i’ 替换为 ‘e’)
enention -> exention (将 ‘n’ 替换为 ‘x’)
exention -> exection (将 ‘n’ 替换为 ‘c’)
exection -> execution (插入 ‘u’)
提示:
0 <= word1.length, word2.length <= 500
word1 和 word2 由小写英文字母组成
思路
还是按照DP三部曲来判断
是不是最值问题?
最少操作步骤,是最值问题
能否使用DP?
- 原问题是否能分解为子问题?
- 有无后效性?
使用DP步骤?
- 确定DP含义
- 确定边界值
- 确定状态转移方程
给定一个单词,转换为另外一个单词
最后一步只有上面三种情况
假设,前面的工作都做好,最后一步是插入一个字符
假设,前面的工作都做好,最后一步是删除一个字符
假设,前面的工作都做好,最后一步是替换一个字符
class Solution {
public int minDistance(String word1, String word2) {
//边界值
if(word1 == null || word2 == null) return 0;
char[] word1CharArray = word1.toCharArray();
char[] word2CharArray = word2.toCharArray();
int word1Length = word1CharArray.length;
int word2Length = word2CharArray.length;
//dp的长度为word1的长度+1
int[][] dp = new int[word1Length + 1][word2Length + 1];
dp[0][0] = 0;
//行为0
for(int col = 1; col <= word2Length; col++){
dp[0][col] = col;
}
//列为0
for(int row = 1; row <= word1Length; row++){
dp[row][0] = row;
}
for(int row = 1; row <= word1Length; row++){
for(int col = 1; col <= word2Length; col++){
//从上到下
int topToBottom = dp[row - 1][col] + 1;
//从左到右
int leftToRight = dp[row][col - 1] + 1;
//从左上到右下
int leftTopToRightBottom = dp[row - 1][col - 1] + 1;
if(word1CharArray[row - 1] == word2CharArray[col - 1]){
leftTopToRightBottom = dp[row - 1][col - 1];
}
dp[row][col] = Math.min(Math.min(topToBottom, leftToRight), leftTopToRightBottom);
}
}
return dp[word1Length][word2Length];
}
}
注意
两层for循环的时候,还是从1开始,而不是从2开始,因为dp[1][1]前面是没有求的
最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
思路
如何判断一个字符串是回文呢?
需要借助两个指针,左边一个指针,右边一个指针。判断两个值是否相等,相等则做相应的加法与减法,不相等则不是回文子串
思路1:暴力法
两个for循环,时间复杂度为O(n^2)
每一个区间都需要判断是否是回文子串,都需要O(n)
因此,整体时间复杂度为O(n^3)
class Solution {
public String longestPalindrome(String s) {
//边界值
if(s == null) return null;
if(s.length() == 0) return "";
char[] sArray = s.toCharArray();
ArrayList list = new ArrayList();
int max = 0;
int resultLeft = 0;
int resultRight = 0;
for(int left = 0; left < sArray.length; left++){
for(int right = left; right < sArray.length; right++){
if(isHuiWenChuan(sArray, left, right)){
if(max < right - left + 1){
max = right - left + 1;
resultLeft = left;
resultRight = right;
}
}
}
}
return new String(sArray, resultLeft, resultRight - resultLeft + 1);
}
private boolean isHuiWenChuan(char[] sArray, int left, int right)
{
int i = left;
int j = right;
while(i < j){
if(sArray[i] == sArray[j]){
i++;
j--;
}else{
return false;
}
}
return true;
}
}
注意:
char[] sArray = s.toCharArray();是char[] 不是int[]
字符串与数组的转换:
字符串转数组
数组转字符串
思路2:DP
DP三大步骤:
是否为最值问题?
最长回文子串,是
能否使用DP解决?
- 原问题能否分解为子问题?
- 无后效性
DP解题步骤
-
确定dp含义
dp[i][j]表示字符串s从i到j是不是回文子串
注意,dp[i][j]存储的是和否 -
确定边界值
dp[0][0]、dp[1][1]、dp[2][2]、…、dp[k][k] = true; -
确定状态转移方程
假如现在已知dp[i - 1][j]的结果,能推出dp[i][j]的结果吗?
假如现在已知dp[i][j - 1]的结果,能推出dp[i][j]的结果吗?
如果有字符串aba,已知ab不是回文,那么aba是回文
如果有字符串abc,已知ab不是回文,那么abc不是回文
如果有字符串aaa,已知aa是回文,那么aaa是回文
如果有字符串aab,已知aa是回文,那么aab不是回文
并不能通过前面的dp[i - 1][j]或dp[i][j - 1]的结果推出dp[i][j]的结果,那,
怎么办呢?
分两种情况考虑:
-
如果s[i, j]的长度:j - i + 1 <= 2
<=2那么,只有0,1,2
0的情况已经在开始排除
1的情况也已经考虑过dp[i, i] = true;
那么,剩下的只有长度为2
如果j - i + 1 = 2
那么dp[i, j] = s[i] == s[j];并且,长度为1的时候,也可以使用这个公式 -
如果s[i, j]的长度:j - i + 1 > 2
如果s[i + 1, j - 1]是回文子串,且s[i] == s[j],那么s[i, j]是回文子串
dp[i, j] = dp[i + 1, j - 1] && s[i] == s[j];
左下角的值,推到出右上角的值
class Solution {
public String longestPalindrome(String s) {
//边界值
if(s == null) return null;
if(s.length() == 0) return "";
//字符转数组
char[] sArray = s.toCharArray();
//建立dp,里面存储的是true,false
boolean [][] dp = new boolean[sArray.length][sArray.length];
int left = 0;
int maxlength = 1;
//先求左下,再求右上,这就要求i需要从大到小,j从小到大
for(int i = sArray.length - 1; i>=0; i--){
for(int j = i; j<sArray.length; j++){
if(j - i + 1 <= 2){
dp[i][j] = (sArray[i] == sArray[j]);
}else{
dp[i][j] = dp[i + 1][j - 1] && (sArray[i] == sArray[j]);
}
if(dp[i][j] && (maxlength < j - i + 1)){
maxlength = j - i + 1;
left = i;
}
}
}
return new String(sArray, left, maxlength);
}
}
注意:
dp[i, j] = dp[i + 1, j - 1] && s[i] == s[j];
先求左下,再求右上
这就要求i需要从大到小,j从小到大
思路3:扩展中心法
以某一个点为中心,左右扩展,左右相等,则是回文子串(下图左)
但,这些子串都是奇数个,回文子串的偶数个没有被计算,因此,需要借助元素之间的间隙,左右扩展,从而找出偶数类型的回文子串(下图右)
假设字符串“abbaba”的长度为n,那么,可以作为扩展中心的个数为:n + (n - 1) = 2*n - 1
时间复杂度为O((2n - 1) * n) = O(2n^2 - n) = O(n ^2)
空间复杂度为O(1)
class Solution {
public String longestPalindrome(String s) {
//边界值
if(s == null) return null;
if(s.length() == 0) return s;
char[] sArray = s.toCharArray();
ArrayList list = new ArrayList();
int max = 1;
int begin = 0;
for(int i = 0; i < sArray.length; i++){
int left = i;
int right = i;
int count = 1;
while(--left >= 0 && ++right < sArray.length){
if(sArray[left] == sArray[right]){
count = count + 2;
if(max < count){
max = count;
begin = left;
}
}else{
break;
}
}
}
for(int i = 0; i < sArray.length - 1; i++){
int left = i;
int right = i + 1;
int count = 0;
while(left >= 0 && right < sArray.length){
if(sArray[left] == sArray[right]){
count = count + 2;
if(max < count){
max = count;
begin = left;
}
}else{
break;
}
left--;
right++;
}
}
return new String(sArray, begin, max);
}
}
思路4:马拉车法
略