我试图找到给定字符串中的所有子字符串。对于像rymis
这样的随机字符串,子序列为[i, is, m, mi, mis, r, ry, rym, rymi, rymis, s, y, ym, ymi, ymis]
。从Wikipedia开始,长度为n
的字符串将包含n * (n + 1) / 2
个总字符串。
可以通过以下代码片段找到:
final Set<String> substring_set = new TreeSet<String>();
final String text = "rymis";
for(int iter = 0; iter < text.length(); iter++)
{
for(int ator = 1; ator <= text.length() - iter; ator++)
{
substring_set.add(text.substring(iter, iter + ator));
}
}
适用于较小的String长度,但由于算法接近O(n^2)
,因此对于较大的长度显然会变慢。
同时阅读可以在O(n)
进行插入的后缀树并注意到相同的子序列可以通过从右边删除1个字符重复插入子字符串直到字符串为空来获得。哪个应该是O(1 + … + (n-1) + n)
,summation of n
- &gt; n(n+1)/2
- &gt; (n^2 + n)/ 2
,又在O(n^2)
附近。虽然似乎有一些后缀树可以在log2(n)
时间内进行插入,这将是一个更好的因素O(n log2(n))
。
在我深入研究后缀树之前,这是正确的路线,是否有其他算法可以提高效率,或者O(n^2)
是否会达到最佳效果?
答案 0 :(得分:1)
这是你的例子的倒置方式,但仍然是o(n ^ 2)。
string s = "rymis";
ArrayList<string> al = new ArrayList<string>();
for(int i = 1; i < s.length(); i++){//collect substrings of length i
for(int k = 0; k < s.length(); k++){//start index for sbstr len i
if(i + k > s.length())break;//if the sbstr len i runs over end of s move on
al.add(s.substring(k, k + i));//add sbstr len i at index k to al
}
}
让我看看我是否可以发布递归示例。我开始做一些递归尝试,并使用双滑动窗口提出这种迭代方法,作为对上述方法的一种改进。我有一个递归的例子,但是在减少树大小方面存在问题。
string s = "rymis";
ArrayList<string> al = new ArrayList<string>();
for(int i = 1; i < s.length() + 1; i ++)
{
for(int k = 0; k < s.length(); k++)
{
int a = k;//left bound window 1
int b = k + i;//right bound window 1
int c = s.length() - 1 - k - i;//left bound window 2
int d = s.length() - 1 - k;//right bound window 2
al.add(s.substring(a,b));//add window 1
if(a < c)al.add(s.substring(c,d));//add window 2
}
}
使用arraylist影响性能时出现了一个问题,因此下一个将使用更基本的结构。
string s = "rymis";
StringBuilder sb = new StringBuilder();
for(int i = 1; i < s.length() + 1; i ++)
{
for(int k = 0; k < s.length(); k++)
{
int a = k;//left bound window 1
int b = k + i;//right bound window 1
int c = s.length() - 1 - k - i;//left bound window 2
int d = s.length() - 1 - k;//right bound window 2
if(i > 1 && k > 0)sb.append(",");
sb.append(s.substring(a,b));//add window 1
if(a < c){
sb.append(",");
sb.append(s.substring(c,d));//add window 2
}
}
}
string s = sb.toString();
String[] sArray = s.split("\\,");
答案 1 :(得分:1)
我很确定你无法击败O(n ^ 2),正如问题评论中提到的那样。
我对不同的编码方式感兴趣,所以我快速编写了一个,我决定在这里发布。
我放在这里的解决方案并不是渐渐没有我想的那么快,但是当计算内部和外部循环时,它的数量会减少。此处的重复插入次数也较少 - 没有重复的插入。
String str = "rymis";
ArrayList<String> subs = new ArrayList<String>();
while (str.length() > 0) {
subs.add(str);
for (int i=1;i<str.length();i++) {
subs.add(str.substring(i));
subs.add(str.substring(0,i));
}
str = str.substring(1, Math.max(str.length()-1, 1));
}
答案 2 :(得分:1)
我不确定确切的算法,但您可以查看Ropes:
http://en.wikipedia.org/wiki/Rope_(computer_science)
总之,当数据很大且频繁修改时,绳索更适合。
我相信Rope在你的问题上胜过String。