您的位置:首页 > 职场人生

中文分词算法-百度面试题

2015-06-15 23:16 549 查看
题目:

给定一个字符串, 一个数组,判断这个字符串能否被分隔成字典里的一个个单词。

用动态规划算法

我面试时写的是下面的代码

public static boolean divied2(String s,String[] dict){
boolean result=false;
if(s.length()==0)
return true;
for (int i = 0; i < dict.length; i++) {
int index=s.indexOf(dict[i]);
if (index!=-1) {
System.out.println(index);
String tmp1=s.substring(0,index);
String tmp2=s.substring(index+dict[i].length(),s.length());
return divied(tmp1+tmp2,dict);
}
}
return result;
}


但是对于测试用例

String[]  dict={"百度一","百度","一下","我就","知","道"};
System.out.println(divied2("百度一下我就知道", dict));


这个是不通过的。因为百度一这个词先删除了,一下这个单词被破坏了,

回来想了一下,上面的原因是终止了遍历。经过改进后,测试通过

原题在于这个|=操作,也就是说对所有的结果进行或操作,有一个可以分隔完全就可以。

public static boolean divied(String s,String[] dict){
boolean result=false;
if(s.length()==0)
return true;
for (int i = 0; i < dict.length; i++) {
int index=s.indexOf(dict[i]);
if (index!=-1) {
System.out.println(index);
String tmp1=s.substring(0,index);
String tmp2=s.substring(index+dict[i].length(),s.length());
result|=divied(tmp1+tmp2,dict);
}
}
return result;
}


缺点是时间复杂度过高,

字符串长为m,字典大小为n

那么时间复杂度为:

n^(m)左右

public static boolean divied(String s,String[] dict){
boolean result=false;
if(s.length()==0)
return true;
for (int i = 0; i < dict.length; i++) {
count++;
int index=s.indexOf(dict[i]);
if (index!=-1) {
System.out.println(index);
String tmp1=s.substring(0,index);
String tmp2=s.substring(index+dict[i].length(),s.length());
result|=divied(tmp1+tmp2,dict);
if (result) {//优化点
return true;
}
}
}
return result;
}


优化思路。在result=true的情况下直接终止循环。

加个全局变量看函数执行次数

不在中断的情况下

函数执行了180次左右(和词曲顺序有关)。

在加了中断之后。

函数只执行了21次。

在不断调整词典顺序的情况下,如果字符串可以分隔完全,函数只执行了最多30次左右。但是如果不可以的话,还是会执行374。

优化思路二:

如果在字符串里每个词只出现了一次,可以直接在找到并删除这个词后将词典里的这个词删除,这样就可以避免不必要的循环

优化思路三:

其实或操作是针对于词典里以同一个字符为开头的长度不同的词设计的。这可以在程序里表现的更针对一点。

改进后如下,效果非常好。无论字符串能不能被分隔完全 ,两种情况的时间复杂度是基本一样的。

对于如下测试用例:可以分隔,执行了44次,不可以的情况是60次

时间复杂度降为词典长度的阶乘。

String[]  dict={"百度一","一下","知","我就","百度","道"};
System.out.println(divied("百度一下我就知道", dict));


String[]  dict={"百度一","一下","知","我就","百度","道"};
System.out.println(divied("百度一下我后就知道", dict));


public static boolean divied(String s,String[] dict){
boolean result=false;
if(s.length()==0)
return true;
char start='\0';
for (int i = 0; i < dict.length; i++) {
count++;
int index=s.indexOf(dict[i]);
if (start=='\0'&&index!=-1||index!=-1&&dict[i].charAt(0)==start) {
System.out.println(index);
String tmp1=s.substring(0,index);
String tmp2=s.substring(index+dict[i].length(),s.length());
start=dict[i].charAt(0);
result|=divied(tmp1+tmp2,dict);
if (result) {
return true;
}
}
}
return result;
}


优化思路四:

对于思路三的改进 ,只在有重复开头的词的情况下才进行递归,其它的删除,继续循环,不递归

public class Divide {

static int count=0;
public static boolean divied(String s,String[] dict){
boolean result=false;
if(s.length()==0)
return true;
char start='\0';
for (int i = 0; i < dict.length; i++) {
count++;
int index=s.indexOf(dict[i]);
if(start=='\0'&&index!=-1){
String tmp1=s.substring(0,index);
String tmp2=s.substring(index+dict[i].length(),s.length());
s=tmp1+tmp2;
start=dict[i].charAt(0);
}
if (index!=-1&&dict[i].charAt(0)==start) {
String tmp1=s.substring(0,index);
String tmp2=s.substring(index+dict[i].length(),s.length());
s=tmp1+tmp2;
result|=divied(tmp1+tmp2,dict);
if (result) {
return true;
}
}
}
return result;
}
public static void main(String[] args){
String[]  dict={"百度一","百度","我就","一下","知","道"};
System.out.println(divied("百度一下我就知道", dict));
System.out.println(count);
}
}


最后成果,对于可以完全分隔的,循环次数为6

对于不可以完全分隔的,循环次数为18
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法