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)表示。
上面的分析主要考虑了主要的项,一些次要项被省略或者未考虑,不会影响最终的阶数或增长速度。
{
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)表示。
上面的分析主要考虑了主要的项,一些次要项被省略或者未考虑,不会影响最终的阶数或增长速度。
相关文章推荐
- 一道算法题的一种O(n)解法
- 关于一种求最大公约数的算法的分析与证明
- 对连连看一种算法的分析与思考
- 一道算法题的一种O(n)解法
- 八皇后问题解法及算法分析
- 斐波那契数列3种解法(朴素递归、动态规划、数学归纳)及算法分析
- NOI2.4基本算法之分治 统计数字 分析----换一种思路
- 关于一种求最大公约数的算法的分析与证明
- 算法分析中递推式的一般代数解法
- 算法分析中递推式的一般代数解法
- [算法设计与分析]4.2.1枚举法(百钱百鸡问题2种解法+数字谜2种解法)
- 关于一种求最大公约数的算法的分析与证明
- 算法分析中递推式的一般代数解法
- 回溯算法 8皇后问题的一种解法 适合初学者观察整个回溯的过程
- [原创] 算法题 Zero Sum的一种解法
- 逻辑回归(Logistic Regression, LR)又称为逻辑回归分析,是分类和预测算法中的一种。通过历史数据的表现对未来结果发生的概率进行预测。例如,我们可以将购买的概率设置为因变量,将用户的
- 一道算法题的一种O(n)解法
- 循环冗余校验 CRC的算法分析和程序实现
- 冒泡排序的算法分析与改进
- Windows脚本编码器算法分析与破译