1. 程式人生 > 實用技巧 >2020.7.16 hdu1516 樸素dp

2020.7.16 hdu1516 樸素dp

Levenshtein 距離(字串距離)

s為初始,t為目標

1、初始化

在i等於0時,說明s串沒有資料,直接一位一位插入資料。

在j等於0時,需要s串一步一步刪除資料。

for(int i=0;i<=n;i++)

        dp[i][0]=i;

for(int i=0;i<=m;i++)

        dp[0][i]=i;

2、轉移方程

要求dp[i][j],需要dp[i-1][j],dp[i][j-1],dp[i-1][j-1]

轉移方式有三種:

1)增加:s[1…i] 轉換為 t[1…j-1],由於t[j]的加入,s串也需要加上t[j],所以dp[i][j]=dp[i][j-1]+1;

2)刪除:s[1..i-1]轉換為t[1..j],直接刪去s[i],所以dp[i][j]=dp[i-1][j]+1;

3)s[1…i-1] 轉換為 t [1…j-1],

  直接比較s[i]和t[j],如果相等,dp[i][j]=dp[i-1][j-1];

  替換:如果不相等,s串的s[i]改為t[j],dp[i][j]=dp[i-1][j-1]+1;

所以dp[i][j]最終結果是三者最小的

for(int i=1;i<=n;i++)

    for(int j=1;j<=m;j++){

        dp[i][j]=min(dp[i-1][j],dp[i][j-1])+1;

        dp[i][j]
=min(dp[i][j],dp[i-1][j-1]+(s[i-1]!=t[j-1])); }

完整程式碼

import java.util.Scanner;

public class Main{
    static String s1,s2;
    static int[][] dp;
    static int n,m;
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        while(sc.hasNext()){
            s1
=sc.next(); s2=sc.next(); n=s1.length(); m=s2.length(); dp=new int[n+1][m+1]; inspect(); change(); } } private static void inspect() { for (int i = 0; i <= n; i++) { dp[i][0]=i; } for (int i = 0; i <= m; i++) { dp[0][i]=i; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { int ij=1; if(s1.charAt(i-1)== s2.charAt(j-1)){ ij=0; } dp[i][j]=Min(dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+ij); } } System.out.println(dp[n][m]); } private static void change() { int i=s1.length(); int j=s2.length(); int step=1; int temp; while(i>=1||j>=1){ if(i>0&&j>0&&s1.charAt(i-1)==s2.charAt(j-1)) temp=0; else temp=1; /* 若是兩個字串中對應字母相等由於沒有進行任何操作只用將上一對的資料遷移便好 若是不相等且所有周邊資料中dp[i-1][j-1]最小,那麼就意味著進行了替換操作 由於僅將這一對字母替換運算元加一所以只用將上一對字母對應資料加一賦給當前便好 */ if(i>=1&&j>=1&&dp[i][j]==dp[i-1][j-1]+temp){ if(temp==1) System.out.println(step+++" Replace "+i+","+s2.charAt(j-1)); i--; j--; } /* 插入原理與刪除類似,因為要插入說明此時y對應的字母與x序列無關,所以 此時運算元dp[i][j]應與dp[i][j-1]有關(x中前i個元素組成序列,與y前j-1元素序列代表的運算元) */ else if(j>0&&dp[i][j]==dp[i][j-1]+1){ System.out.println(step+++" Insert "+(i+1)+","+s2.charAt(j-1)); j--; } /* 此種情況代表刪除 刪除操作的資料,應與{x1...x(i-1)}與{y1...y(j-1)}有關,因為刪除就意味著當前 x序列中的xi與此時的y序列無關,而x序列前i-1項是已經進行完所有操作對於x轉為y有 最短的距離,所以此時運算元dp[i][j]應與dp[i-1][j]有關,而且因為做了刪除操作所以運算元加一 */ else if(i>0&&dp[i][j]==dp[i-1][j]+1){ System.out.println(step+++" Delete "+i); i--; } } } private static int Min(int i, int i1, int i2) { int min=i; if(i1<min) min=i1; if(i2<min) min=i2; return min; } }