是这样的:一个中文数组, 一个key, 要求是在数组中用二分查找找到key.当然这不难
但是要求还有:在数组中找到以key为前缀的所有词, 要求一个字一个字的用二分,比如"大白兔" 数组排序后,二分找到第一个字是"大"的范围,在这个范围继续二分找第二个字是"白"这样. 这样逐字二分查找最终找到,key为前缀的 所有词.
接下来要求是 key是一句话,用上述方法找出这句话中的最长的词(数组中的词)
我真是一脸懵逼了.
问题来源如下:
https://wenku.baidu.com/view/f749b351f01dc281e53af094.html
汉字的字典树还真没写过。不过可以试试。你这个问题也太复杂了吧?有三个要求?
1.查找中文数组中的key
2.在数组中找到所有key为前缀的词
3.在key中找到一个最长的词(出现在数组中)
这三个要求其实是独立的三个问题,对吧
如果你愿意等,我下班后去写一写。
——————————————————————2018.3.30更新——————————————————
一开始使用c语言,后来发现char数组和指针再结合汉字是一个让你脑袋能炸掉的组合。所以用了c++。时间复杂度时间复杂度O((logn)^2) 。实现的比较简单,仅有几个功能函数,各种变量也是有点随意。测试结果如下:
// // main.cpp // sss // // Created by 小康 on 29/03/2018. // Copyright © 2018 小康. All rights reserved. // #include <iostream> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <map> using namespace std; //字典树,根节点为1 struct Node { int length=0; string lines=""; map<string,int> words; map<string,int> count; }tree[1005]; //表示字典树的节点数 int len; string res[105]; /*char* word(char *aw,int j) { char w[105]={'\0'}; int p=0; for(int i=j*3;i<j*3+3;i++) { w[p++]=aw[i]; } return w; } void strcatt(char *as,char *bs,int j) { int l=strlen(as); for(int i=l;i<l+3;i++) { as[i]=bs[j*3+i-l]; } }*/ //往字典树立插入字符串 void insert(string a,int i,int j) { if(i==a.length()/3) { return; } if(tree[j].words[a.substr(i*3,3)]!=0) { insert(a,i+1,tree[j].words[a.substr(i*3,3)]); } else { tree[j].words[a.substr(i*3,3)]=++len; tree[j].lines+=a.substr(i*3,3); tree[j].length+=3; if(i==a.length()/3-1) tree[j].count[a.substr(i*3,3)]++; insert(a,i+1,len); } } /* bool equal(char *ae,char *be) { if(strlen(ae)!=strlen(be)) return false; else { for(int i=0;i<strlen(ae);i++) { if(ae[i]!=be[i]) return false; } return true; } }*/ void remove(Node &s,string x) { int y=0; for(int i=0;i<s.length/3;i++) { if(s.lines.substr(i*3,3)==x) { y=i;break; } } for(int i=y*3;i<s.length;i++) { s.lines[i]=s.lines[i+3]; } s.length-=3; } //删除字符串 bool del(string a,int i,int j) { if(tree[j].words[a.substr(i*3,3)]==0) return false; else { int x=tree[j].words[a.substr(i*3,3)]; tree[j].words[a.substr(i*3,3)]=0; remove(tree[j],a.substr(i*3,3)); return del(a,i+1,x); } } //查询某个字符串为前缀的所有词 void QueryPrefix(string a,int i,int j,string str,int &l2) { if(i>=a.length()/3) { if(tree[j].length==0) { return; } for(int k=0;k<tree[j].length/3;k++) { str+=tree[j].lines.substr(k*3,3); if(tree[j].count[tree[j].lines.substr(k*3,3)]!=0) res[l2++]=str; QueryPrefix(a, i+1, tree[j].words[tree[j].lines.substr(k*3,3)],str,l2); } } else if(tree[j].words[a.substr(i*3,3)]==0) return; else { //strcatt(str,aq,i); str+=a.substr(i*3,3); if(i==a.length()/3-1) res[l2++]=str; QueryPrefix(a, i+1, tree[j].words[a.substr(i*3,3)],str,l2); } } //查询某个字符串是否存在 bool IsExist(string a,int i,int j) { if(i==a.length()/3) { return false; } if(tree[j].words[a.substr(i*3,3)]==0) return false; else { if(i==a.length()/3-1&&tree[j].count[a.substr(i*3,3)]!=0) { return true; } return IsExist(a, i+1,tree[j].words[a.substr(i*3,3)]); } } string input[1005]; int n; string output; int main() { printf("请输入要插入字典树的字符串数组的长度\n"); scanf("%d",&n); printf("请输入要插入字典树的字符串数组\n"); len=1; for(int i=0;i<n;i++) { cin>>input[i]; insert(input[i],0,1); } printf("请输入key\n"); cin>>output; printf("查找key是否存在\n"); IsExist(output,0, 1); if(IsExist(output,0,1)) printf("key存在\n"); else printf("key不存在\n"); printf("找出所有以key为前缀的字符串\n"); string str=""; int l1=0;int l2=0; QueryPrefix(output, 0, 1,str, l2); for(int i=0;i<l2;i++) cout<<res[i]<<endl; printf("key为一句话,找出key中存在的最长词\n"); printf("输入key\n"); cin>>output; int le=output.length(); string xx=""; string ans=""; int res2=0; for(int i=0;i<le/3;i++) { for(int l=3;i*3+l<=le;l+=3) { xx=output.substr(i*3,l); if(IsExist(xx, 0, 1)) { if(res2<l) { res2=l; ans=xx; } } } } if(res2==0) { printf("不存在\n"); } else { cout<<ans<<endl; } return 0; }
是的 后边的链接是个论文,其实我是想实现里边的 第三个方法:基于逐字二分的XXXX,,像里边提到的例子一样实现,但是也一直是懵逼状态, 自己对java的理解也不足 没找到好的办法;
先谢谢你了,麻烦了.
@燃烧的小腿毛: 我写的是基于TRIE索引树的汉字字典树哦~
@Shendu.cc: 那也很好,正好论文里边提到的都想学一下,我描述的可能有不大准确的,方便的话你看下论文里边那个例子,那个说的就很明白
@燃烧的小腿毛: 好的👌
@燃烧的小腿毛: 在不在。今天上午有事情,刚才补好了。