算法从入门到放弃——第八期 分词

本文探讨了NLP中的基本任务,如动态规划实现的分词算法,利用编辑距离衡量词间相似度,并介绍了Bigram分词的应用。通过LeetCode实例展示如何解决相关问题,强调算法基础在实际项目中的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天周末休息,娱乐娱乐在看NLP,NLP中最基本的过程之一是分词,也是构建LM并训练词向量的基础。

这里面有几个问题,第一,就是分词问题,这里我们不提IK,JieBa这些中文分词器,我们只去讨论leetcode中的题目,从简单的动态规划入手,到bigram,这是第一部分

第二,比如说一个词和另一个词的相似程度,比如app,apple,apply的相似程度我以怎样的方式去表示。最简单的一种方式就是比如今天要说的编辑距离。比如我认为apple变为apply需要编辑1个字符,而apple变为app需要改变两个字符,那么apple和apply的相似程度就比apple和app要高。这leetcode中也有类似题目,对应的直接就是编辑距离

所以再有人跟你说,刷leetcode没有用,那么你一定得清楚,刷leetcode是为了打好算法的基础,因为在此基础上构建的生产算法,是你的目标,你连基础都搞不掂,还搞个屁?你刷以为没用,那一定是你学的不够多,因为学的越多的人,都觉得自己越无知,学的越少的人,往往觉得自己什么都知道。

进入正题:

1,分词

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。

注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。
示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。
     注意,你可以重复使用字典中的单词。

做dp题,是有套路的,大致过程得有:确定dp含义,画理想中的dp结果,写dp方程,确定边界条件,debug实现

确定dp含义,dp[i]表示前i个字母是否可以被词典分

那么对于s = "leetcode", wordDict = ["leet", "code"],其dp数组可以写出来

dp[] = {0,0,0,1,0,0,0,1},为了方便写作00010001,

那么dp方程

dp[i] = dp[i-k]&&words.contains(subString(i-k,i)),k表示截取单词长度

eg:leetcode 那么对于dp[i],k表示从0到i截取字符串

对dp[3],也就是lee需要从,substring(0,1),substring(0,2),substring(0,3)判断,到底dict里有没有这个串,有就true,没有就false

综上,代码

public static boolean wordBreak(String s, List<String> wordDict) {
        boolean[] dp = new boolean[s.length() + 1];
        for (int i = 0; i <= s.length(); i++) {
            for (int k = 0; k <= i; k++) {
                String substring = s.substring(i - k, i);
                if (k == i) {
                    if (wordDict.contains(s.substring(0, i))) {
                        dp[i] = true;
                    }
                }else {
                    dp[i] = dp[i - k] && wordDict.contains(substring);
                }
                if (dp[i]) {
                    break;
                }
            }
        }
        return dp[dp.length - 1];
    }

2、编辑距离

给你两个单词 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')

    public int minDistance(String word1, String word2) {
        if (word1.length() == 0 || word2.length() == 0) {
            return word1.length() + word2.length();
        }
        int length1 = word1.length() ;
        int length2 = word2.length() ;
        int[][] dp = new int[length1 + 1][length2 + 1];
        for (int i = 0; i <= length1; i++) {
            dp[i][0] = i;
        }
        for (int i = 0; i <= length2; i++) {
            dp[0][i] = i;
        }
        for (int i = 1; i <= length1; i++) {
            for (int j = 1; j <= length2; j++) {
                if (word1.charAt(i-1) == word2.charAt(j-1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = Math.min(Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + 1);
                }
            }
        }
        return dp[length1][length2];
    }

不会的同学,可以直接去leetcode看解答,我为什么想把编辑距离放在这有两点:

1、动规在NLP里有应用场景,而且不少,所以别觉得动规没有应用场景

2、针对动规本身而言,两个单词,往往都是构造dp[i][j],i表示第一个单词的前i个字母,j表示第二个单词的前j个字母

3、Bigram分词

给出第一个词 first 和第二个词 second,考虑在某些文本 text 中可能以 "first second third" 形式出现的情况,其中 second 紧随 first 出现,third 紧随 second 出现。

对于每种这样的情况,将第三个词 "third" 添加到答案中,并返回答案。

示例 1:

输入:text = "alice is a good girl she is a good student", first = "a", second = "good"
输出:["girl","student"]
示例 2:

输入:text = "we will we will rock you", first = "we", second = "will"
输出:["we","rock"]

Bigram是词法的基础,一般unigram是指单个单词,Trigram是指三个单词下一个,leetcode只是抛砖引玉,就这道题而言,做法很多,穷举就可以了,我这里也只是提出一个问题供大家思考:Bigram的训练集,为Bigram提供了得到第三个单词的基础,假设我们的训练集足够大,按理说能够包含所有的语言所拥有的情况,我们是不是可以通过前两个单词,预测下一个单词的走向,那我们是不是又可以通过三个单词构造一个句子?但是问题来了,怎么样一个句子才算通顺,怎么样才符合人的思维,这才是NLU关注的点,也就是词的特性——词向量。NLP的核心就是围绕词向量展开的,由于我也是新手上路,就不献丑了,留个大家自己学。

代码很简单,穷举就完事了:

class Solution {
    public String[] findOcurrences(String text, String first, String second) {
        String[] words = text.split(" ");
        List<String> list = new ArrayList<String>();
        for (int i = 2; i < words.length; i++) {
            if (words[i - 2].equals(first) && words[i - 1].equals(second)) {
                list.add(words[i]);
            }
        }
        int size = list.size();
        String[] ret = new String[size];
        for (int i = 0; i < size; i++) {
            ret[i] = list.get(i);
        }
        return ret;
    }
}

 但愿我有一天有能力和有时间来把NLP这个大坑给大家填一填

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值