检查一个句子是否包含单词列表中的单词

时间:2017-11-08 14:34:26

标签: java regex collections text-classification multilabel-classification

我想检查句子是否包含映射到类别的单词列表中的单词。所以我有一个KeyValue.java类,包含单词,类别名称和方法filterCategory来检查它是否包含单词。现在我有10,000个关键字为文本映射了不同的类别。但问题是它是缓慢的方式。你能否提出一些其他方法来加快分类。谢谢你的帮助。

public class KeyValue {
private String key;
private String value;

public KeyValue(String key, String value) {
    this.key = key;
    this.value= value;
}
public KeyValue() {
}
public String getKey() {
    return key;
}
public void setKey(String key) {
    this.key = key;
}
public String getValue() {
    return value;
}
public void setValue(String value) {
    this.value = value;
}

Classification.java

class Classification
{

private static List<KeyValue> keyMap = new ArrayList<KeyValue>();

static{
    getWordMap();
}

public static List<KeyValue> getWordMap()
{           
    if(keyMap.size()==0)
    {
        keyMap.add(new KeyValue("sports","football"));
        keyMap.add(new KeyValue("sports","basketball"));
        keyMap.add(new KeyValue("sports","olympics"));
        keyMap.add(new KeyValue("sports","cricket"));
        keyMap.add(new KeyValue("sports","t20"));
    }
}

public static KeyValue filterCategory(String filteredText)
{               
    KeyValue kv = null;

    for(KeyValue tkv:keyMap)
    {
        String value = tkv.getValue();
        String lc = filteredText.toLowerCase();
        lc = FormatUtil.replaceEnglishSymbolsWithSpace(lc);//remove symbols with space and then normalizes it

        String lastWord="";
        if(lc.contains(" "))
        {
            lastWord = lc.substring(lc.lastIndexOf(" ")+1);

            if(lc.startsWith(value+" ") || lc.contains(" "+value+" ") || value.equals(lastWord))
            {
                kv = new KeyValue(tkv.getKey(), tkv.getValue());
                break;
            }               
        }
        else if(lc.contains(value))
        {
            kv = new KeyValue(tkv.getKey(), tkv.getValue());
            break;              
        }
    }

    if(kv==null)
    {
        return new KeyValue("general","0");
    }
    else 
    {
        kv.setValue("100");
        return kv;
    }
}
}

3 个答案:

答案 0 :(得分:0)

我不明白为什么你不使用Java的util.Map来解决这个问题,但我建议你迭代使用:

lc = FormatUtil.replaceEnglishSymbolsWithSpace(lc);
String result= Arrays.stream(lc.split(" ")).filter(s -> s.equals(value)).findFirst().orElse("");
            if(result.length()>0) {
                kv = tkv;
            }

答案 1 :(得分:0)

您的实施是合理的,但对KeyValue对象使用Exhaustive or Brute-Force Search算法,而不是使用HashingHashMap or Hashtable对象等更快的匹配算法。

<强>假设

  • 您有 10,000 映射的字词。
  • 你试图将这些单词与英语句子或短语匹配,例如&#34; 快速的棕色狐狸跳过懒狗&#34;

问题

你的逻辑,正如所写的那样,将执行强力搜索,尝试为你的句子中的每个单词进行10,000次匹配。如果句子中的每个单词都不存在于KeyValue对象中,则使用上面给出的短语将创建(10,000)x(9)= 90,000 最大尝试次数。

此逻辑会创建Big-O的最差情况或Θ(n)性能点击,其中 n 表示列表中的字数。这称为线性搜索。对此方法的一种惰性改进是使用排序列表,为您提供更好的Θ(log(n)) 对数搜索时间。

修复

使用散列算法,而不是执行强力搜索,一次对整个单词执行查找;或者,如果您想通过字符移位执行模式匹配,请查看Rabin—Karp Hash Algorithm。在简单匹配整个单词的情况下,您的算法会将您的句子单词分解为标记(就像您现在所使用的那样),然后对您的值和相关类别的散列图使用散列函数查找。

您的新逻辑将具有Θ(1)的Big-O性能。这种恒定时间匹配将大大提高您的应用速度。

<强>伪代码

// Adapting your KeyValue into a simple <Value, Key> map e.g. <"football", "sports">
//HashMap<String, String> map = new HashMap<String, String>();

// Adapting your KeyValue into a <Value, Set<Key>> map for multiple 
//    category keys e.g. <"football", <"sports","sunday","games">>
HashMap<String, Set<String>> map = new HashMap<String, Set<String>>();

// build the hashmap with your values and categories
Set<String> categories = new HashSet<String>();
categories.add("sports");
categories.add("sunday");
categories.add("games");
map.put("football", categories);
...

// sanitize your input
String lc = filteredText.toLowerCase();
lc = FormatUtil.replaceEnglishSymbolsWithSpace(lc);

// tokenize your sentence
String[] tokens = lc.split("\\s");
...

// search tokens against your hashmap
for (String token : tokens) {

    // search the token against the hashmap
    if (map.containsKey(token)){
        Set<String> cats = map.get(token);
        ...
    } else {
        ...
    }
}

答案 2 :(得分:0)

根据建议,我发布了最快的代码,我可以提出。

基于KeyValue的List已被修改为简单的HashMap

private static HashMap<String,String> map = new HashMap<String,String>();

感谢您的建议。它现在可以扩展到投入生产。

public static KeyValue filterCategory(String filteredText)
{               
    KeyValue kv = null;
    filteredText = filteredText.toLowerCase();
    filteredText = FormatUtil.replaceEnglishSymbolsWithSpace(filteredText);

    StringTokenizer tokenizer = new StringTokenizer(filteredText);
    while(tokenizer.hasMoreTokens()) {
        String temp = tokenizer.nextToken();
        if(map.containsKey(temp))
        {
            kv = new KeyValue(map.get(temp),"100");
            break;
        }
    }       
    if(kv==null)
    {
        kv= new KeyValue("general","0");
    }
    return kv;
}