自动补足算法是什么_输入自动提示与补全功能的设计与实现

本文介绍了一种实现输入自动提示与补全的方法,特别是针对Java关键字的自动补全功能。通过创建前缀匹配映射,提高了效率,避免了遍历整个单词列表。文章详细阐述了算法设计和代码实现,包括简单的实现和性能更好的实现,并提供了Servlet的使用示例。
摘要由CSDN通过智能技术生成

包含一个简短而完整的Web示例, 演示如何根据用户输入的字符进行自动提示和补全。

一、

场景与目标

在使用 IDE 开发软件时, IDE 会提供一种“智能提示”, 根据所输入的字符列出可能的词组; 在日常Web开发中,根据用户输入进行自动提示和补全,也能很好地改善使用体验。本文实现输入自动提示与补全功能。

输入自动补全功能实际上是“前缀匹配问题”, 即给定一个前缀以及一个单词列表, 找出所有包含该前缀的单词。

本文实现的功能是: 根据用户输入的关键字, 给出与之匹配的 Java 关键字。

二、 算法与设计

最简单直观的方案莫过于直接遍历单词列表, 检测每个单词是否包含前缀, 并返回。这样做的缺点是, 每次都要遍历单词列表, 效率非常低下。 一个更好的思路是, 先构建一个前缀匹配映射 Map>, key 是每一个单词中所包含的前缀, value 是包含该 key 的所有单词列表。 那么, 问题就转化为给定一个单词列表 list, 将其转换为 Map> , 这里 Word, Prefix, Matcher

均为 String 类型。

一种思路是, 遍历每一个单词包含的每一个前缀, 找出所有包含该前缀的单词。

for word in words

for prefix in word(0,i)

for word in words

if (word.startWith(prefix)) {

result.put(prefix, result.get(prefix).add(word));

}

显然, 其效率是 O(总前缀数*总单词数), 在单词列表比较大的情况下, 其效率是比较低的。 要想避免这种嵌套遍历, 就必须充分利用每一次遍历,获取充分的信息。

另一种思路是, 先找出每个单词中所包含的前缀匹配对, 再将这些前缀匹配对合并为最终的前缀匹配映射。 类似 Map - Reduce  方式。

for word in words

for prefix in word(0,i)

pairs.add(new Pair(prefix, word))

mergePairs(pairs)

其效率是O(总的前缀数)。

三、 代码设计与实现

下面给出代码设计与实现。 注意到, 这是通过多次小步重构达到的结果, 而不是一次性实现。 具体是, 先写出一个最简单的实现, 可以把应用跑起来; 然后, 思考更有效率的实现, 最后进行了抽象。

1.  定义接口

packageautocomplete;importjava.util.Set;public interfacePrefixMatcher {

SetobtainMatchedWords(String inputText);

}

2.  定义抽象类

1 packageautocomplete;2

3 importjava.util.Collections;4 importjava.util.HashMap;5 importjava.util.HashSet;6 importjava.util.Map;7 importjava.util.Set;8

9 public abstract class AbstractPrefixMatcher implementsPrefixMatcher {10

11 protected final String[] javaKeywords = newString[] {12 "abstract", "assert",13 "boolean", "break", "byte",14 "case", "catch", "char", "class", "const", "continue",15 "default", "do", "double",16 "else", "enum", "extends",17 "final", "finally", "float", "for",18 "goto",19 "if", "implements", "import", "instanceof", "int", "interface",20 "long",21 "native", "new",22 "package", "private", "protected", "public",23 "return",24 "strictfp", "short", "static", "super", "switch", "synchronized",25 "this", "throw", "throws", "transient", "try",26 "void", "volatile",27 "while"

28 };29

30 protected Map> prefixMatchers = new HashMap>();31

32 abstract voiddynamicAddNew(String inputText);33

34 public SetobtainMatchedWords(String inputText) {35 Set matchers =prefixMatchers.get(inputText);36 if (matchers == null) {37 Set input = new HashSet();38 input.add(inputText);39 dynamicAddNew(inputText);40 returninput;41 }42 returnmatchers;43 }44

45 protected Map>obtainPrefixMatchers() {46 returnCollections.unmodifiableMap(prefixMatchers);47 }48

49 }

3.  简单的实现

packageautocomplete;importjava.util.HashMap;importjava.util.HashSet;importjava.util.Map;importjava.util.Set;public class SimpleWordMatcher extendsAbstractPrefixMatcher {publicSimpleWordMatcher() {

prefixMatchers=buildPrefixMatchers(javaKeywords);

}/*** 将输入的单词组转化为前缀匹配的映射

*@paramkeywords

*@return*

* eg. {"abc", "acd", "bcd"} ===>

* {"a": ["abc", "acd"], "ab": ["abc"], "abc": ["abc"],

* "ac": ["acd"], "acd": ["acd"], "b": ["bcd"], "bc": ["bcd"], "bcd": ["bcd"]

* }*/

public Map>buildPrefixMatchers(String[] keywords) {

HashMap> prefixMatchers = new HashMap>();for(String keyword: keywords) {int wordLen =keyword.length();for (int i=1; i < wordLen; i++) {

String prefix= keyword.substring(0, i);for(String keyword2: javaKeywords) {if(keyword2.startsWith(prefix)) {

Set matchers =prefixMatchers.get(prefix);if (matchers == null) {

matchers= new HashSet();

}

matchers.add(keyword2);

prefixMatchers.put(prefix, matchers);

}

}

}

}returnprefixMatchers;

}public static voidmain(String[] args) {

SimpleWordMatcher wordMatcher= newSimpleWordMatcher();

MapUtil.printMap(wordMatcher.obtainPrefixMatchers());

String[] prefixes= new String[] {"a", "b", "c", "d", "e", "f", "g", "i","l", "n", "p", "r", "s", "t", "v", "w", "do", "finally"};for(String prefix: prefixes) {

System.out.println(wordMatcher.obtainMatchedWords(prefix));

}

}

@OverridevoiddynamicAddNew(String inputText) {

}

}

4.  性能更好的实现

packageautocomplete;importjava.util.ArrayList;importjava.util.HashMap;importjava.util.HashSet;importjava.util.Iterator;importjava.util.List;importjava.util.Map;importjava.util.Set;public class EffectiveWordMatcher extendsAbstractPrefixMatcher {publicEffectiveWordMatcher() {

prefixMatchers=buildPrefixMatchers(javaKeywords);

}static classPair {privateString key;privateString value;publicPair(String key, String value) {this.key =key;this.value =value;

}publicString getKey() {returnkey;

}publicString getValue() {returnvalue;

}publicString toString() {return "";

}

}private Map>buildPrefixMatchers(String[] javakeywords) {

List pairs =strarr2pairs(javakeywords);returnmergePairs(pairs);

}/** 将 字符串数组转化为前缀匹配对

* eg. ["ab", "ac"] ===>

* [, , , ]*/

private Liststrarr2pairs(String[] javakeywords) {

List pairs = new ArrayList();for(String keyword: javakeywords) {int wordLen =keyword.length();for (int i=1; i < wordLen; i++) {

String prefix= keyword.substring(0, i);

Pair pair= newPair(prefix, keyword);

pairs.add(pair);

}

}returnpairs;

}/** 将多个 合并为一个映射

* eg. [, , , , ] ===>

* {"a"=>["abstract", "assert", "b"=>["boolean", "break"], "c"=>["continue"]}*/

private static Map> mergePairs(Listpairs) {

Map> result = new HashMap>();if (pairs != null && pairs.size() > 0) {for(Pair pair: pairs) {

String key=pair.getKey();

String value=pair.getValue();

Set matchers =result.get(key);if (matchers == null) {

matchers= new HashSet();

}

matchers.add(value);

result.put(key, matchers);

}

}returnresult;

}

@OverridevoiddynamicAddNew(String inputText) {if(checkValid(inputText)) {

List newpairs = strarr2pairs(newString[] {inputText});

Map> newPreixMatchers =mergePairs(newpairs);

mergeMap(newPreixMatchers, prefixMatchers);

}

}private booleancheckValid(String inputText) {return false;

}private Map> mergeMap(Map> src, Map>dest) {

Set>> mapEntries =src.entrySet();

Iterator>> iter =mapEntries.iterator();while(iter.hasNext()) {

Map.Entry> entry =iter.next();

String key=entry.getKey();

Set newMatchers =entry.getValue();if(dest.containsKey(key)) {

dest.get(key).addAll(newMatchers);

}else{

dest.put(key, newMatchers);

}

}returndest;

}public static voidmain(String[] args) {

EffectiveWordMatcher wordMatcher= newEffectiveWordMatcher();

MapUtil.printMap(wordMatcher.obtainPrefixMatchers());

String[] prefixes= new String[] {"a", "b", "c", "d", "e", "f", "g", "i","l", "n", "p", "r", "s", "t", "v", "w", "do", "finally"};for(String prefix: prefixes) {

System.out.println(wordMatcher.obtainMatchedWords(prefix));

}

}

}

5.   Servlet 使用

packageservlets;importjava.io.IOException;importjava.util.Set;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importautocomplete.EffectiveWordMatcher;importautocomplete.PrefixMatcher;public class AutoCompleteServlet extendsHttpServlet {protected PrefixMatcher wordMatcher = newEffectiveWordMatcher();public voiddoGet(HttpServletRequest req, HttpServletResponse resp)throwsServletException, IOException {

doPost(req, resp);

}public voiddoPost(HttpServletRequest req, HttpServletResponse resp)throwsServletException, IOException {

resp.setContentType("text/plain;charset=UTF8");

String inputText= req.getParameter("inputText");

Set matchers =wordMatcher.obtainMatchedWords(inputText);

StringBuilder sb= newStringBuilder();for(String m: matchers) {

sb.append(m);

sb.append(' ');

}

sb.deleteCharAt(sb.length()-1);

resp.getWriter().print(sb.toString());

}

}

6.  前端交互

输入自动补全功能演示

$.ajax(

{

url:'servlets/AutoCompleteServlet',

data: {'inputText': inputText },

dataType:'text',

timeout:10000,

success:function(data) {if(keycode== 13) {//Enter

$('#inputText').val($('#matchedKeywords').val());

$('#resultRegion').empty();return;

}if(keycode== 38 ||keycode== 40) {//上下箭头

$('#matchedKeywords').trigger('focus');return;

}

$('#resultRegion').empty();varmatchers=data.split(' ');if(matchers.length> 0 &&inputText!= '') {

$('#resultRegion').append('')

$('#matchedKeywords').append('' + '' + '');for(i=0; i

$('#matchedKeywords').append('' +keyword+ '');

}

$('#matchedKeywords').attr('size', matchers.length+1);

$('#matchedKeywords').height(20*(matchers.length+1));

$('#matchedKeywords').click(function() {

$('#inputText').val($('#matchedKeywords').val());

$('#resultRegion').empty();

});

}

}

}

);

}

$(this).bind("keyup",function(eventObj) {varkeycode=eventObj.which;

searchMatchers(keycode);

});

});

#main{margin:15% 20% 0% 25%;

}#inputText{width:450px;height:30px;

}#matchedKeywords{width:450px;height:25px;

}#resultRegion{text-align:left;margin:0 0 0 128px;

}

输入自动补全功能演示,请输入Java关键字:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值