动态规划应用--搜索引擎拼写纠错

文章目录

    • 1. 字符串相似度
      • 1.1 莱文斯坦距离
      • 1.2 最长公共子串长度
    • 2. 计算编辑距离
      • 2.1 莱文斯坦距离
      • 2.2 最长公共子串长度
    • 3. 搜索引擎拼写纠错
    • 4. 练习题

在 Trie树那节讲过,利用Trie可以进行关键词提示,节省输入时间。
在搜索框中你不小心打错了字,它也会智能提醒你是不是要搜XXX

1. 字符串相似度

  • 如何量化两个字符串的相似度?有一个非常著名方法,编辑距离(Edit Distance)
  • 编辑距离,将一个字符串转化成另一个字符串,需要的最少编辑操作次数(增加一个字符、删除一个字符、替换一个字符)。
  • 编辑距离越大,两个字符串相似度越小;编辑距离越小,两个字符串相似度越大。两个完全相同的字符串,编辑距离是0。

1.1 莱文斯坦距离

  • 莱文斯坦距离(Levenshtein distance)允许增加删除替换字符这三个编辑操作
  • 莱文斯坦距离的大小,表示两个字符串差异的大小

1.2 最长公共子串长度

  • 最长公共子串长度(Longest common substring length)只允许增加删除字符这两个编辑操作
  • 表示两个字符串相似程度的大小

两个字符串 mitcmu 和 mtacnu 的莱文斯坦距离是3,最长公共子串长度是4。
在这里插入图片描述

2. 计算编辑距离

这个问题是求把一个字符串变成另一个字符串,需要的最少编辑次数。整个求解过程,涉及多个决策阶段,需要依次考察一个字符串中的每个字符,跟另一个字符串中的字符是否匹配,所以,问题符合多阶段决策最优解模型。

2.1 莱文斯坦距离

回溯解法:
回溯是一个递归处理的过程。如果a[i]与b[j]匹配,我们递归考察a[i+1]和b[j+1]。如果a[i]与b[j]不匹配,那我们有多种处理方式可选:

  • 可以删除a[i],然后递归考察a[i+1]和b[j];
  • 可以删除b[j],然后递归考察a[i]和b[j+1];
  • 可以在a[i]前面添加一个跟b[j]相同的字符,然后递归考察a[i]和b[j+1];
  • 可以在b[j]前面添加一个跟a[i]相同的字符,然后递归考察a[i+1]和b[j];
  • 可以将a[i]替换成b[j],或者将b[j]替换成a[i],然后递归考察a[i+1]和b[j+1]。
/*** @description: 莱文斯坦距离,回溯* @author: michael ming* @date: 2019/7/25 1:25* @modified by: */
#include <string>
#include <iostream>
using namespace std;
void lwstBT(string &a, string &b, int i, int j, int dist, int &minDist)
{if(i == a.size() || j == b.size()){if(i < a.size())dist += (a.size()-i);if(j < b.size())dist += (b.size()-j);if(dist < minDist)minDist = dist;return;}if(a[i] == b[j])// 两个字符匹配{lwstBT(a,b,i+1,j+1,dist,minDist);}else// 两个字符不匹配{lwstBT(a,b,i+1,j,dist+1,minDist);// 删除 a[i] 或者 b[j] 前添加一个字符lwstBT(a,b,i,j+1,dist+1,minDist);// 删除 b[j] 或者 a[i] 前添加一个字符lwstBT(a,b,i+1,j+1,dist+1,minDist);// 将 a[i] 和 b[j] 替换为相同字符}
}
int main()
{int minDist = INT_MAX;string a = "mitcmu", b = "mtacnu";lwstBT(a,b,0,0,0,minDist);cout << "莱文斯坦距离:" << minDist << endl;
}

在这里插入图片描述
在这里插入图片描述

  • 在递归树中,每个节点状态包含三个变量(i,j,dist),dist表示处理到a[i]和b[j]时,已经执行的编辑次数。

  • 在递归树中,(i,j)两个变量重复的节点很多,比如(3,2)和(2,3)。对于(i,j)相同的节点,我们只保留 dist最小的,继续递归处理,剩下的舍弃。所以,状态就从(i,j,dist)变成了(i,j,min_dist),其中min_dist 表示处理到a[i]和b[j],已经执行的最少编辑次数。

  • 这个问题的状态转移方式,要比之前两节课中讲到的例子要复杂很多。上一节我们讲的矩阵最短路径问题中,到达状态(i,j)只能通过(i-1,j)或(i,j-1)两个状态转移过来,而今天这个问题,状态(i,j)可能从(i-1,j),(i,j-1),(i-1,j-1)三个状态中的任意一个转移过来。
    在这里插入图片描述
    写状态转移方程

    如果:a[i] != b[j],那么:min_dist(i, j) 就等于:
    min{min_dist(i-1,j)+1,  min_dist(i,j-1)+1,  min_dist(i-1,j-1)+1}如果:a[i] == b[j],那么:min_dist(i, j) 就等于:
    min{min_dist(i-1,j)+1,  min_dist(i,j-1)+1, min_dist(i-1,j-1)}其中,min 表示求三数中的最小值。     
    

    根据状态转移方程,填充状态表
    在这里插入图片描述

/*** @description: 莱文斯坦距离,动态规划* @author: michael ming* @date: 2019/7/25 20:40* @modified by: */
#include <string>
#include <iostream>
using namespace std;
int min(int x, int y, int z)
{int m = INT_MAX;if(x < m)   m = x;if(y < m)   m = y;if(z < m)   m = z;return m;
}
int lwstDP(string &a, const int lenA, string &b, const int lenB)
{int i, j;int minDist[lenA][lenB];for(j = 0; j < lenB; ++j)//初始化第 0 行:a[0..0] 与 b[0..j] 的编辑距离{if(a[0] == b[j])minDist[0][j] = j;else if(j != 0)minDist[0][j] = minDist[0][j-1]+1;elseminDist[0][j] = 1;}for(i = 0; i < lenA; ++i)//初始化第 0 列:a[0..i] 与 b[0..0] 的编辑距离{if(a[i] == b[0])minDist[i][0] = i;else if(i != 0)minDist[i][0] = minDist[i-1][0]+1;elseminDist[i][0] = 1;}for(i = 1; i < lenA; ++i)//按行填状态表for(j = 1; j < lenB; ++j){if(a[i] == b[j])minDist[i][j] = min(minDist[i-1][j]+1, minDist[i][j-1]+1, minDist[i-1][j-1]);elseminDist[i][j] = min(minDist[i-1][j]+1, minDist[i][j-1]+1, minDist[i-1][j-1]+1);}return minDist[lenA-1][lenB-1];
}
int main()
{string a = "mitcmu", b = "mtacnu";cout << "莱文斯坦距离:" << lwstDP(a,a.size(),b,b.size()) << endl;
}

2.2 最长公共子串长度

最长公共子串作为编辑距离的一种,只允许增加删除字符两种操作。表征的也是两个字符串之间的相似程度。

  • 每个状态包括三个变量(i,j,max_lcs),max_lcs表示a[0…i]和b[0…j]的最长公共子串长度。那(i,j)这个状态都是由哪些状态转移过来的呢?
  • 先来看回溯的处理思路。我们从a[0]和b[0]开始,依次考察两个字符串中的字符是否匹配。
  • 如果a[i]与b[j]匹配,最大公共子串长度+1,继续考察a[i+1]和b[j+1]。
  • 如果a[i]与b[j]不匹配,最长公共子串长度不变,这个时候,有两个不同的决策路线:
    1 删除a[i],或者在b[j]前面加上一个字符a[i],然后继续考察a[i+1]和b[j];
    2 删除b[j],或者在a[i]前面加上一个字符b[j],然后继续考察a[i]和b[j+1]。

a[0…i]和b[0…j]的最长公共长度max_lcs(i,j),只有可能通过下面三个状态转移过来:
(i-1,j-1,max_lcs),max_Ics表示 a[0…i-1] 和 b[0…j-1] 的最长公共子串长度;
(i-1,j,max_lcs),max_lcs表示 a[0…i-1] 和 b[0…j] 的最长公共子串长度;
(i,j-1,max_lcs),max_Ics表示 a[0…i] 和 b[0…j-1] 的最长公共子串长度。

状态方程
如果:a[i] == b[j],那么:max_lcs(i, j) 就等于:
max{max_lcs(i-1,j-1)+1, max_lcs(i-1, j), max_lcs(i, j-1)};如果:a[i] != b[j],那么:max_lcs(i, j) 就等于:
max{max_lcs(i-1,j-1), max_lcs(i-1, j), max_lcs(i, j-1)};其中 max 表示求三数中的最大值。
/*** @description: 最长公共子串长度, DP* @author: michael ming* @date: 2019/7/25 21:40* @modified by: */
#include <string>
#include <iostream>
using namespace std;
int max(int x, int y, int z)
{int m = INT_MIN;if(x > m)   m = x;if(y > m)   m = y;if(z > m)   m = z;return m;
}
int lcsDP(string &a, const int lenA, string &b, const int lenB)
{int i, j;int maxlcs[lenA][lenB];for(j = 0; j < lenB; ++j)//初始化第 0 行:a[0..0] 与 b[0..j] 的maxlcs{if(a[0] == b[j])maxlcs[0][j] = 1;else if(j != 0)maxlcs[0][j] = maxlcs[0][j-1];elsemaxlcs[0][j] = 0;}for(i = 0; i < lenA; ++i)//初始化第 0 列:a[0..i] 与 b[0..0] 的maxlcs{if(a[i] == b[0])maxlcs[i][0] = 1;else if(i != 0)maxlcs[i][0] = maxlcs[i-1][0];elsemaxlcs[i][0] = 0;}for(i = 1; i < lenA; ++i)//按行填状态表for(j = 1; j < lenB; ++j){if(a[i] == b[j])maxlcs[i][j] = max(maxlcs[i-1][j], maxlcs[i][j-1], maxlcs[i-1][j-1]+1);elsemaxlcs[i][j] = max(maxlcs[i-1][j], maxlcs[i][j-1], maxlcs[i-1][j-1]);}return maxlcs[lenA-1][lenB-1];
}
int main()
{string a = "mitcmu", b = "mtacnu";cout << "最大公共子串长度:" << lcsDP(a,a.size(),b,b.size()) << endl;
}

最大公共子串长度:4

3. 搜索引擎拼写纠错

  • 当用户在搜索框内,输入一个拼写错误的单词时,拿这个单词跟词库中的单词一—进行比较,计算编辑距离,将编辑距离最小,作为纠正之后的单词,提示用户。

  • 这就是拼写纠错最基本的原理。真正商用的搜索引擎,拼写纠错功能不会这么简单。一方面,单纯利用编辑距离来纠错,效果并不一定好;另一方面,词库中的数据量可能很大,对纠错的性能要求很高。

  • 针对纠错效果不好,有很多种优化思路。
    a. 取出编辑距离最小的TOP10,然后根据其他参数决策选择。比如使用搜索热门程度来决定。
    b. 可以用多种编辑距离计算方法,比如今天讲到的两种,分别计算编辑距离最小的TOP10,求交集,用交集的结果,再继续优化处理。
    c. 可以统计用户的搜索日志,得到最常拼错的单词列表,以及对应的拼写正确的单词。纠错时,首先在这个最常拼错单词列表中查找。一旦找到,直接返回对应的正确的单词。这样纠错的效果非常好。
    d. 引入个性化因素。针对每个用户,维护这个用户特有的搜索喜好,也就是常用的搜索关键词。当用户输入错误时,先在这个用户常用的搜索关键词中,计算编辑距离,查找编辑距离最小的单词。

  • 针对纠错性能方面,讲两种分治的优化思路。
    a. 如果纠错功能的TPS(每秒事务处理量(TransactionPerSecond))不高,可以部署多台机器,每台机器运行一个独立的纠错功能。当有一个纠错请求的时候,通过负载均衡,分配到其中一台机器,来计算编辑距离,得到纠错单词。
    b. 如果纠错系统的响应时间太长,也就是,每个纠错请求处理时间过长,可以将纠错的词库,分割到很多台机器。当有一个纠错请求的时候,将这个拼写错误的单词,同时发送到多台机器,并行处理,分别得到编辑距离最小的单词,然后再比对合并,最终决定出一个最优的纠错单词。

4. 练习题

LeetCode 72. 编辑距离(DP)

LeetCode 1143. 最长公共子序列(动态规划)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/480060.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

玩转算法之面试第十章-贪心算法

leetcode 455 分配饼干 尝试将最大的饼干给最贪心的朋友 如果满足&#xff0c;则1 如果不满足&#xff0c;则将最大的饼干给次贪心的朋友&#xff0c;一次类推 试图让最多的小朋友开心 在这里插入代码片 #include<iostream> #include<vector>using namespace …

论文浅尝 | 基于知识库的自然语言理解 04#

本文转载自公众号&#xff1a;知识工场。罗康琦&#xff0c;上海交通大学计算机系2019届博士&#xff0c;研究方向为自然语义理解和知识图谱。2012年获得华中科技大学软件工程学士学位&#xff0c;现就职于京东数据科学实验室&#xff08;Data Science Lab&#xff09;。他曾在…

BERT跨模态之后:占领了视觉常识推理任务榜单TOP 2!

星标/置顶小屋&#xff0c;带你解锁最萌最前沿的NLP、搜索与推荐技术文 | 小鹿鹿lulu编 | YY前言由于 BERT-like 模型在 NLP 领域上的成功&#xff0c;研究者们开始尝试将其应用到更为复杂的 多模态 任务上。要求模型除文本数据以外&#xff0c;还要接收其他模态的数据&#xf…

常见的算法面试问题以及代码实现

1 时间复杂度分析 一个简单的时间测试代码如下&#xff1a; #include<iostream> #include<cmath> #include<ctime>using namespace std;int main(){for(int x1;x<9;x){int npow(10,x);clock_t startTimeclock();int sum0;for(int i0;i<n;i)sumi;clock…

阿里P8架构师谈:高并发与多线程的关系、区别、高并发的技术方案

什么是高并发&#xff1f; 高并发&#xff08;High Concurrency&#xff09;是一种系统运行过程中遇到的一种“短时间内遇到大量操作请求”的情况&#xff0c;主要发生在web系统集中大量访问收到大量请求&#xff08;例如&#xff1a;12306的抢票情况&#xff1b;天猫双十一活动…

LeetCode 1. 两数之和(哈希)

文章目录1. 题目2. 解题1. 暴力解法2. 哈希法3. python3解答1. 题目 题目链接&#xff1a;https://leetcode-cn.com/problems/two-sum/ 给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个 整数&#xff0c;并返回他们的数组下标。…

论文浅尝 | TuckER:基于张量分解的知识图谱补全

笔记整理&#xff1a;孙泽群&#xff0c;南京大学计算机科学与技术系&#xff0c;博士研究生。论文链接&#xff1a;https://arxiv.org/abs/1901.09590背景知识图谱是图结构的数据库&#xff0c;以三元组(es, r, eo)的形式存储事实&#xff0c;其中es和eo分别表示主语和宾语实体…

阿里Java P系列技术要求(P5-P7)

阿里p系列薪资&#xff08;最新数据比这个高1倍左右&#xff09; 阿里P5&#xff08;高级研发工程师&#xff09; 工作要求&#xff1a; 能独立完成日常工作&#xff0c;并能够对一些方案提出自己的建议。 基本考核就是能上手独立完成工作&#xff0c;熟练掌握。 技能要求…

如何配置一台深度学习工作站?

星标/置顶小屋&#xff0c;带你解锁最萌最前沿的NLP、搜索与推荐技术这篇文章主要介绍的是家用的深度学习工作站&#xff0c;典型的配置有两种&#xff0c;分别是一个 GPU 的机器和四个 GPU的机器。如果需要更多的 GPU 可以考虑配置两台四个 GPU 的机器。如果希望一台机器同时具…

LeetCode 2. 两数相加(单链表反转)

题目链接&#xff1a;https://leetcode-cn.com/problems/add-two-numbers/ 给出两个 非空 的链表用来表示两个非负的整数。其中&#xff0c;它们各自的位数是按照 逆序 的方式存储的&#xff0c;并且它们的每个节点只能存储 一位 数字。 如果&#xff0c;我们将这两个数相加起…

报名 | 全国知识图谱与语义计算大会(CCKS 2019)评测任务发布

全国知识图谱与语义计算大会是由中国中文信息学会语言与知识计算专委会定期举办的全国年度学术会议。CCKS 2018吸引了来自学术界和工业界的超800人参加。2019 年全国知识图谱和语义计算大会(www.ccks2019.cn) 将于2019年8月24日至8月27日在杭州召开&#xff0c;CCKS 2019的主题…

UML是什么?UML常用图以及建模工具有哪些?

“ 在做项目设计方案的时候&#xff0c;理解为需求后&#xff0c;我们都会做技术设计方案&#xff0c;这个时候就需要用到UML建模&#xff0c;涉及到UML常用图形以及工具画图&#xff0c;以下我会详细介绍UML图形和我个人推荐的UML建模工具。 UML即Unified Model Language&am…

炼丹必备!推荐一个超级好用的机器学习云平台

矩池云是一个专业的国内深度学习云平台&#xff0c;拥有着良好的深度学习云端训练体验&#xff0c;和高性价比的自建GPU集群资源。高性价比矩池云拥有很高的性价比&#xff0c;其的计费方式主要分为按时租与按周/月租。按时租用采用的是分钟级的实时计费模式&#xff0c;满足了…

直通BAT必考题系列:深入详解JVM内存模型与JVM参数详细配置

JVM基本是BAT面试必考的内容&#xff0c;今天我们先从JVM内存模型开启详解整个JVM系列&#xff0c;希望看完整个系列后&#xff0c;可以轻松通过BAT关于JVM的考核。 BAT必考JVM系列专题 1.JVM内存模型 2.JVM垃圾回收算法 3.JVM垃圾回收器 4.JVM参数详解 5.JVM性能调优 JV…

论文浅尝 | 基于平行新闻的Bootstrapping关系抽取

笔记整理&#xff1a;吴锐&#xff0c;东南大学大四本科生&#xff0c;研究方向为自然语言处理。Citation&#xff1a;Michael Glass, K. B. . (2012). Bootstrapping relation extraction using parallel news articles. Retrieved from https://pdfs.semanticscholar.org/bfa…

动态规划应用--最长递增子序列 LeetCode 300

文章目录1. 问题描述2. 解题思路2.1 动态规划2.2 二分查找1. 问题描述 有一个数字序列包含n个不同的数字&#xff0c;如何求出这个序列中的最长递增子序列长度&#xff1f;比如2&#xff0c;9&#xff0c;3&#xff0c;6&#xff0c;5&#xff0c;1&#xff0c;7这样一组数字序…

论文浅尝 | 5 篇顶会论文带你了解知识图谱最新研究进展

本文转载自公众号&#xff1a;PaperWeekly。精选 5 篇来自 ICLR 2019、WSDM 2019、EMNLP 2018、CIKM 2018和IJCAI 2018 的知识图谱相关工作&#xff0c;带你快速了解知识图谱领域最新研究进展。WSDM 2019■ 论文解读 | 张文&#xff0c;浙江大学在读博士&#xff0c;研究方向为…

LeetCode 53. 最大子序和(动态规划)

文章目录1. 题目描述2. 解题2.1 暴力求解2.2 动态规划1. 题目描述 题目链接&#xff1a;https://leetcode-cn.com/problems/maximum-subarray/ 《剑指Offer》同题&#xff1a;面试题42. 连续子数组的最大和 给定一个整数数组 nums &#xff0c;找到一个具有最大和的连续子数组…

java程序员的发展之路和职业规划

在互联网做技术的朋友&#xff0c;往往没有足够的重视&#xff0c;职业规划其实一点都不虚&#xff0c;而是一件非常实在的事情&#xff0c;如果你不是每次碰墙再反思职业规划&#xff0c;而是提前3年左右作出下一步的规划&#xff0c;你早已经走出了一条属于自己的路。 以下是…

图Graph--拓扑排序(Topological Sorting)

文章目录1. 拓扑排序2. 算法实现2.1 Kahn算法2.2 DFS算法2.3 时间复杂度3. 应用4. 类似题目练习一个项目往往会包含很多代码源文件。编译器在编译整个项目时&#xff0c;需按照依赖关系&#xff0c;依次编译每个源文件。比如&#xff0c;A.cpp依赖B.cpp&#xff0c;那在编译时&…