您的位置:首页 > 其它

wordBreak一种解法的算法分析

2014-06-30 11:58 162 查看
bool wordBreak(string s, unordered_set<string> &dic)

{

std::unordered_set<std::string>::const_iterator get;

int sLength = s.size();

int right = 0;

string tmp;

stack<int> index;

int start;

while (true)

{

tmp += s[right];

get = dic.find(tmp);

if (get != dic.end())

{

index.push(right);

tmp.clear();

if (right == sLength - 1)return true;

}

if (right == sLength - 1)

{

if (index.empty())return false;

right = index.top();

index.pop();

start = index.empty() ? 0 : index.top() + 1;

tmp = string(s, start, right - start + 1);

}

right += 1;

}

}

这道程序来自leetcode的wordbreak,这个解法是很粗糙的,会出现超时。现在分析一下程序开销

这个程序是首先匹配最短的单词,比如字典里有 word和words,输入串是words,那么word会被首先选择,最后回溯移动会匹配到words
栈index记录每个单词最后一个字母的下标
tmp用于匹配,成功则清空,失败则增加下一个字幕继续匹配
如果匹配到串的最后不成功,就最后一个成功的单词增加一个,再向后匹配
一次匹配不成功,要么是因为不可能成功,或者是中间使用的较短的/不合适的单词

假设 串长度为s,字典里共有n个单词

一 空间开销

最差的情况,串种的每个字母有独立匹配,空间消耗和串长度一样,所以是O(s)

二 时间开销

在实际测试中,运行时间非常长。直接分析比较困难,所以采用递推的方式来分析。

假设第一次匹配到末尾时为:s1,s2,........sn-1,sn,k。si是匹配成功的单词的长度,b是最后一次匹配的长度,b这个串可能匹配成功也可能失败。已经完成的循环次数为:s1+s2+.....+sn-1+sn+k = s。如果匹配失败,还需回退。

最简单的情况:最后一个匹配成功的单词(子串),增加下一个子母,然后继续向后匹配。index栈size在不断递减,直到最后一次,index为空,而且匹配串tmp是从0开始直道末尾,tmp等于s,而且匹配不成功,这是就可以确定匹配失败,退出循环。需要的循环次数为:

k

+ sn+k

+sn-1+sn +k

......

+ s3+s4+....+sn+k

s2+s3+.......+k

=nk+(n-1)sn+(n-2)sn-1+.....+3s4+2s3+s2 .如果取si = s/n 这个结果近似是O(ns)

最复杂的情况:回退之后再往后匹配还会匹配成功若干单词,但是最终还是失败,最极端的情况:s = aaaaaaaaaaaaaaaaaa末尾再加一个b:sb,‘’s‘’中含有若干个a,简记为s个。dic = a,aa,aaa,aaaa,aaaaa,aaaaaa,aaaaaaa,aaaaaaaa.........n个逐渐增加的单词。记sb的循环次数为C(s),考虑asb的循环次数:

C(s)+

C(s-1)

.............

C(s-n)

这是一个比斐波那契数列增长还要快速的序列。通项C(s-n)的意义为:第一个字母和后面的n个字母合在一起成为一个匹配的单词,后面的串的循环次数就可以用C(s-n)表示。

上面的分析主要考虑了主要的项,一些次要项被省略或者未考虑,不会影响最终的阶数或增长速度。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: