github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/algorithm/datastructures/dp/char_correction_test.go (about)

     1  package dp
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  )
     7  
     8  /*
     9  字符校正
    10  
    11  
    12  中文字符的校正
    13  中文纠错很多时候是通过拼音进行的,比如 "刘得花"->"liudehua"->"刘德华".
    14  拼音检索方法也有很多,比如可以把热门词汇的拼音字母组织成Trie树,每个热词的结尾汉字
    15  的最后一个拼音字母就是叶子,整体性能就是O(n)的,n为query的拼音总长度. 除了拼音外
    16  也有根据字形(二维文字版的编辑距离?)甚至语义等做的纠错策略。
    17  传统搜索引擎中的查询词智能提示、纠错、同义词、近义词、同好词、相关搜索、知识图谱
    18  等系列功能统称为用户的意图识别模块。
    19  
    20  */
    21  
    22  // 1. 莱温斯坦距离 Levenshtein distance
    23  
    24  /*
    25  增加一个字符、删除一个字符、替换一个字符 编辑距离都+1 也就是 str1[i]!=str2[i] 编辑距离就+1
    26  回溯法方式
    27  i 串i的指针
    28  j 串j的指针
    29  editDis 编辑距离
    30  minDis 最小编辑距离
    31  */
    32  func lvstBT(i, j, editDis int, stri, strj []rune, minDis *int) {
    33  	leni := len(stri)
    34  	lenj := len(strj)
    35  	if i == leni || j == lenj { // 出口
    36  		if i < leni {
    37  			editDis += leni - i
    38  		}
    39  		if j < lenj {
    40  			editDis += lenj - j
    41  		}
    42  		if editDis < *minDis {
    43  			*minDis = editDis
    44  		}
    45  		return
    46  	}
    47  	if stri[i] == strj[j] { // 相等编辑距离 +0
    48  		lvstBT(i+1, j+1, editDis, stri, strj, minDis)
    49  	} else { // 不相等的情况下 , 就进 (i+1,j) (i,j+1) (i+1,j+1) 比较尝试
    50  		lvstBT(i+1, j, editDis+1, stri, strj, minDis)
    51  		lvstBT(i, j+1, editDis+1, stri, strj, minDis)
    52  		lvstBT(i+1, j+1, editDis+1, stri, strj, minDis)
    53  	}
    54  }
    55  
    56  // 动态规划的写法 ,
    57  // 递推式  stri[i]==strj[j] minDis = min(dis(i-1,j) , dis(i,j-1) ,dis(i-1,j-1))
    58  // 		  stri[i]!=strj[j] minDis = min(dis(i-1,j) , dis(i,j-1) ,dis(i-1,j-1))+1
    59  func lvstDP(stri, strj []rune) int {
    60  	leni := len(stri)
    61  	lenj := len(strj)
    62  	dp := make([][]int, leni)
    63  	for j := range dp {
    64  		dp[j] = make([]int, lenj)
    65  	}
    66  	if stri[0] == strj[0] {
    67  		dp[0][0] = 0
    68  	} else {
    69  		dp[0][0] = 1
    70  	}
    71  	// 求出首行 首列的值
    72  	for i := 1; i < leni; i++ { //首列
    73  		if stri[i] == strj[0] {
    74  			dp[i][0] = dp[i-1][0]
    75  		} else {
    76  			dp[i][0] = dp[i-1][0] + 1
    77  		}
    78  	}
    79  	for j := 1; j < lenj; j++ { //首行
    80  		if stri[0] == strj[j] {
    81  			dp[0][j] = dp[0][j-1]
    82  		} else {
    83  			dp[0][j] = dp[0][j-1] + 1
    84  		}
    85  	}
    86  	// 依次填表
    87  	for i := 1; i < leni; i++ {
    88  		for j := 1; j < lenj; j++ {
    89  			if stri[i] == strj[j] {
    90  				dp[i][j] = min3(dp[i-1][j-1], dp[i-1][j], dp[i][j-1])
    91  			} else {
    92  				dp[i][j] = min3(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1
    93  			}
    94  		}
    95  	}
    96  	return dp[leni-1][lenj-1]
    97  }
    98  
    99  func min3(a, b, c int) int {
   100  	return min(a, min(b, c))
   101  }
   102  
   103  func Test_lvst(t *testing.T) {
   104  	stri := []rune("mitcmu")
   105  	strj := []rune("mtacnu")
   106  	editDis1 := 0x7fffffff
   107  	lvstBT(0, 0, 0, stri, strj, &editDis1)
   108  	t.Log("最小编辑距离 editDis1:", editDis1)
   109  
   110  	editDis2 := lvstDP(stri, strj)
   111  	t.Log("最小编辑距离 editDis2:", editDis2)
   112  }
   113  
   114  // 2. 最长公共子串 Longest common substring length
   115  // 最长公共子串长度只允许增加、删除字符这两个编辑操作
   116  // 只要当 str1[i] == str2[i] 或str1[i] == str2[i+1] 或str1[i+1] == str2[i] 这三种情况都算长度+1
   117  // 回溯写法
   118  func lcsBT(i, j, lcs int, stri, strj []rune, maxLen *int) {
   119  	leni := len(stri)
   120  	lenj := len(strj)
   121  	if i == leni || j == lenj { // 出口
   122  		if lcs > *maxLen {
   123  			*maxLen = lcs
   124  		}
   125  		return
   126  	}
   127  	if stri[i] == strj[j] {
   128  		lcsBT(i+1, j+1, lcs+1, stri, strj, maxLen)
   129  	} else {
   130  		lcsBT(i, j+1, lcs, stri, strj, maxLen)
   131  		lcsBT(i+1, j, lcs, stri, strj, maxLen)
   132  		lcsBT(i+1, j+1, lcs, stri, strj, maxLen)
   133  	}
   134  }
   135  
   136  // 动态规划的方式
   137  func lcsDP(stri, strj []rune) int {
   138  	leni := len(stri)
   139  	lenj := len(strj)
   140  	dp := make([][]int, leni)
   141  	for j := range dp {
   142  		dp[j] = make([]int, lenj)
   143  	}
   144  	if stri[0] == strj[0] {
   145  		dp[0][0] = 1
   146  	} else {
   147  		dp[0][0] = 0
   148  	}
   149  
   150  	// 求出首行 首列的值
   151  	for i := 1; i < leni; i++ { //首列
   152  		if stri[i] == strj[0] && i == 1 {
   153  			dp[i][0] = dp[i-1][0] + 1
   154  		} else {
   155  			dp[i][0] = dp[i-1][0]
   156  		}
   157  	}
   158  	for j := 1; j < lenj; j++ { //首行
   159  		if stri[0] == strj[j] && j == 1 {
   160  			dp[0][j] = dp[0][j-1] + 1
   161  		} else {
   162  			dp[0][j] = dp[0][j-1]
   163  		}
   164  	}
   165  	for i := 1; i < leni; i++ {
   166  		for j := 1; j < lenj; j++ {
   167  			if stri[i] == strj[j] {
   168  				dp[i][j] = max3(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1
   169  			} else {
   170  				dp[i][j] = max3(dp[i-1][j-1], dp[i-1][j], dp[i][j-1])
   171  			}
   172  		}
   173  	}
   174  	return dp[leni-1][lenj-1]
   175  }
   176  
   177  func max(a, b int) int {
   178  	if a > b {
   179  		return a
   180  	}
   181  	return b
   182  }
   183  
   184  func max3(a, b, c int) int {
   185  	return max(a, max(b, c))
   186  }
   187  
   188  func Test_lcs(t *testing.T) {
   189  	stri := []rune("mitcmu")
   190  	strj := []rune("mtacnu")
   191  	lsc1 := 0
   192  	lcsBT(0, 0, 0, stri, strj, &lsc1)
   193  	t.Log(" 最长公共子串 lsc1:", lsc1)
   194  
   195  	lsc2 := lcsDP(stri, strj)
   196  	t.Log(" 最长公共子串 lsc2:", lsc2)
   197  }
   198  
   199  /*
   200  我们有一个数字序列包含 n 个不同的数字,如何求出这个序列中的最长递增子序列长度?比如
   201  2, 9, 3, 6, 5, 1, 7 这样一组数字序列,它的最长递增子序列就是 2, 3, 5, 7,所以最长递增子序列
   202  的长度是 4
   203  */
   204  
   205  func longestSubsequence(arr []int) int {
   206  	n := len(arr)
   207  	dp := make([]int, n) // dp 下标的状态当数组长度, 里面的值表示数组当前长度是对于最大的递增串的长度
   208  	for i := range dp {
   209  		dp[i] = 1
   210  	}
   211  	for i := 1; i < n; i++ {
   212  		for j := i - 1; j >= 0; j-- {
   213  			if arr[i] >= arr[j] { //倒序遍历,只要找到 后面比前面大的 就+1
   214  				dp[i] = dp[j] + 1
   215  				break
   216  			} else if dp[i] == 1 {
   217  				dp[i] = dp[i-1]
   218  			}
   219  		}
   220  	}
   221  	fmt.Println(dp)
   222  	return dp[n-1]
   223  }
   224  
   225  func Test_longestSubsequenceBT(t *testing.T) {
   226  	arr := []int{2, 9, 3, 6, 5, 1, 7}
   227  	ret := longestSubsequence(arr)
   228  	t.Log("ret:", ret)
   229  }