Javascript 分词算法

Javascript 分词算法,javascript,dynamic-programming,Javascript,Dynamic Programming,我正在尝试实现“分词”算法 问题: 给定一个非空字符串s和一个包含非空单词列表的字典单词DICT,确定s是否可以分割为一个或多个字典单词的空格分隔序列 注: 在分词过程中,词典中的同一单词可能会重复使用多次。 您可以假定词典中没有重复的单词 例如: Input: s = "leetcode", wordDict = ["leet", "code"] Output: true Explanation: Return true becaus

我正在尝试实现“分词”算法

问题: 给定一个非空字符串s和一个包含非空单词列表的字典单词DICT,确定s是否可以分割为一个或多个字典单词的空格分隔序列

注:

在分词过程中,词典中的同一单词可能会重复使用多次。 您可以假定词典中没有重复的单词

例如:

Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".
我的解决方案:

var wordBreak = function(s, wordDict) {
    if(!wordDict || wordDict.length === 0)
        return false;
    
    while(wordDict.length > 0 || s.length > 0) {
        const word = wordDict.shift();
        const index = s.indexOf(word);
        if(index === -1) {
            return false;
        }
        s = s.substring(0, index) + s.substring(index+word.length, s.length);
    }
    
    return s.length === 0 && wordDict.length === 0 ? true : false;
};
function wordBreak(s, wordDict) {
    const len = s.length;
    const memoization_array_words = new Array(len).fill(null);
    const memoization_array_scores = new Array(len).fill(0);

    const wordScores = {};
    wordDict.forEach(function(word) {
        wordScores[word] = 1
    });

    automata = new AhoCorasick(wordDict);
    results = automata.search(s);

    results.forEach(function(result) {
        // result[0] contains the end position
        // result[1] contains the list of words ending in that position
        const end_pos = result[0];
        result[1].forEach(function(word) {
            const prev_end_pos = end_pos - word.length;
            const prev_score = (prev_end_pos == -1) ? 0 : memoization_array_scores[prev_end_pos];
            const score = prev_score + wordScores[word];
            if (score > memoization_array_scores[end_pos]) {
                memoization_array_words[end_pos] = word;
                memoization_array_scores[end_pos] = score;
            }
        });
    });

    if (memoization_array_words[len-1] == null) {
        return false;
    }
    solution = []
    var pos_to_keep = len - 1;
    while (pos_to_keep >= 0) {
        const word = memoization_array_words[pos_to_keep];
        solution.push(word);
        pos_to_keep -= word.length;
    }
    return solution.reverse()
}
它适用于上面的示例(输入)。但是,以下输入失败

Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
             Note that you are allowed to reuse a dictionary word.
我如何记录我已经删除的单词,并在最后检查它们。在上面的输入中,剩余的s字符串包含单词词典中的“apple”,因此输出应该为true

谢谢

扩展版:如果测试字符串中有一个单词(indexOf==0)开始,我将使用some对wordDict进行测试。如果是这样,我将字符串缩短到单词的长度,并使用缩短的字符串递归调用函数。否则字符串不可拆分,返回false。我一直这样做,直到出现错误或字符串长度为0,我赢了,因为一切正常。

备注:当分词符与s=“cars”wordDict=[“car”、“ca”、“rs”]不明显相似时的错误现已修复。为此,我递归地调用一些方法和算法。因此,如果一种方法在结束前停止,我会后退并寻找替代方法,直到找到一种,否则就没有可能了。

对……的评论;数组。一些
在array.forEach中,如果不使用一些丑陋的技巧(如try…catch和抛出错误),就不能使用break,因此我可以使用for循环的经典变体。但是存在数组。有些方法类似于forEach循环,但只有一个元素返回true,因此结果为true。

例如:

常量数组=[1,2,3,4,5]; //检查元素是否为偶数 常量偶数=(元素)=>元素%2===0; log(array.some(偶数))
您可能还可以通过对数组进行预排序并使用二进制搜索来优化dict上的循环,但希望这能让您了解要点。

如果您要寻找动态编程解决方案,我们将使用数组进行录制,然后我们将循环并跟踪单词

这将在JavaScript中传递:

const wordBreak = function(s, wordDict) {
    const len = s.length
    const dp = new Array(len + 1).fill(false)
    dp[0] = true
    for (let i = 1; i < len + 1; i++) {
        for (let j = 0; j < i; j++) {
            if (dp[j] === true && wordDict.includes(s.slice(j, i))) {
                dp[i] = true
                break
            }
        }
    }

    return dp[s.length]
}
类似地,在Java中,我们会使用
布尔[]


public final class Solution {
    public static final boolean wordBreak(
        String s,
        List<String> words
    ) {
        if (s == null || s.length() == 0) {
            return false;
        }

        final int len = s.length();
        boolean[] dp = new boolean[len];

        for (int i = 0; i < len; i++) {
            for (int j = 0; j <= i; j++) {
                final String sub = s.substring(j, i + 1);

                if (words.contains(sub) && (j == 0 || dp[j - 1])) {
                    dp[i] = true;
                    break;
                }
            }
        }

        return dp[len - 1];
    }
}

公共最终课程解决方案{
公共静态最终布尔断字(
字符串s,
单字
) {
如果(s==null | | s.length()==0){
返回false;
}
最终整数长度=s.长度();
布尔值[]dp=新的布尔值[len];
对于(int i=0;i对于(int j=0;j这是我两年前在另一个上下文中遇到的一个有趣的问题,即查询标记化。在我的例子中,字典中的单词数量大约为数百万,因此每次递归查找字典中的不同单词是不可行的。此外,我需要应用dynamic编程以严格的效率理由解决任务

首先,我建议您使用查找字符串中的单词。该算法在字符串长度的线性时间内查找字符串中任意数量的模式,而不考虑要查找的模式数量(没有更多的字数乘以字符串操作的长度,事实上,在字符串中找到一个字需要扫描整个字符串..)。 幸运的是,我找到了该算法的javascript实现

使用上面链接的代码和动态编程跟踪字符串中出现的单词,我编写了以下javascript解决方案:

var wordBreak = function(s, wordDict) {
    if(!wordDict || wordDict.length === 0)
        return false;
    
    while(wordDict.length > 0 || s.length > 0) {
        const word = wordDict.shift();
        const index = s.indexOf(word);
        if(index === -1) {
            return false;
        }
        s = s.substring(0, index) + s.substring(index+word.length, s.length);
    }
    
    return s.length === 0 && wordDict.length === 0 ? true : false;
};
function wordBreak(s, wordDict) {
    const len = s.length;
    const memoization_array_words = new Array(len).fill(null);
    const memoization_array_scores = new Array(len).fill(0);

    const wordScores = {};
    wordDict.forEach(function(word) {
        wordScores[word] = 1
    });

    automata = new AhoCorasick(wordDict);
    results = automata.search(s);

    results.forEach(function(result) {
        // result[0] contains the end position
        // result[1] contains the list of words ending in that position
        const end_pos = result[0];
        result[1].forEach(function(word) {
            const prev_end_pos = end_pos - word.length;
            const prev_score = (prev_end_pos == -1) ? 0 : memoization_array_scores[prev_end_pos];
            const score = prev_score + wordScores[word];
            if (score > memoization_array_scores[end_pos]) {
                memoization_array_words[end_pos] = word;
                memoization_array_scores[end_pos] = score;
            }
        });
    });

    if (memoization_array_words[len-1] == null) {
        return false;
    }
    solution = []
    var pos_to_keep = len - 1;
    while (pos_to_keep >= 0) {
        const word = memoization_array_words[pos_to_keep];
        solution.push(word);
        pos_to_keep -= word.length;
    }
    return solution.reverse()
}
其中,当我们遇到出现在前一个单词之后或字符串开头的单词时,
memorization\u array\u单词
memorization\u array\u分数
从左到右填充。代码应该是自动复制的,但是如果您需要任何解释,请给我写一条注释。
另外,我给每个单词加了一个分数(为了简单起见,这里是1)这允许您区分不同的解决方案。例如,如果您将每个单词与一个重要性分数相关联,您将以分数最高的标记化结束。在上面的代码中,标记化的单词数最高。

是否允许在dict中有其他单词类似的单词[“部分”,“合作伙伴”]?你真的想使用
shift
?这会修改你的
wordDict
,使其无法重复使用。另外,你是想问输入字符串是否只包含
wordDict
中的子字符串,还是想在
wordDict
中的每个单词实例周围加上空格?换句话说,你对h字符串“leetecode”(在leet和code之间加上一个“e”)你能解释一下some方法吗?顺便说一下,输入s=“cars”wordDict=[“car”,“ca”,“rs”].这应该是真的。好吧,这件事越来越复杂了,我必须去寻找它。从这个问题上看,你也想要这个还不清楚。对不起,我应该解释得更好。我正在考虑用一个哈希表来整理字典中的单词,但我不清楚yet@myTest532myTest532我扩展了新情况并解释了some-method。它返回str.startsWith不是一个function@myTest532myTest532根据MDN的兼容性数据(),该数据只应出现在Internet Explorer中。您可以使用
str.substring(0,word.length)替换startsWith调用===word
。您能解释一下您的解决方案吗?我的意思是,您对问题的理解和方法,而不是leetcode解决方案或其他leetcode解决方案
function wordBreak(s, wordDict) {
    const len = s.length;
    const memoization_array_words = new Array(len).fill(null);
    const memoization_array_scores = new Array(len).fill(0);

    const wordScores = {};
    wordDict.forEach(function(word) {
        wordScores[word] = 1
    });

    automata = new AhoCorasick(wordDict);
    results = automata.search(s);

    results.forEach(function(result) {
        // result[0] contains the end position
        // result[1] contains the list of words ending in that position
        const end_pos = result[0];
        result[1].forEach(function(word) {
            const prev_end_pos = end_pos - word.length;
            const prev_score = (prev_end_pos == -1) ? 0 : memoization_array_scores[prev_end_pos];
            const score = prev_score + wordScores[word];
            if (score > memoization_array_scores[end_pos]) {
                memoization_array_words[end_pos] = word;
                memoization_array_scores[end_pos] = score;
            }
        });
    });

    if (memoization_array_words[len-1] == null) {
        return false;
    }
    solution = []
    var pos_to_keep = len - 1;
    while (pos_to_keep >= 0) {
        const word = memoization_array_words[pos_to_keep];
        solution.push(word);
        pos_to_keep -= word.length;
    }
    return solution.reverse()
}