您的位置:首页 > 其它

给一个字符串S和一个字符串数组T(T中的字符串要比S短许多),设计一个算法, 在字符串S中查找T中的字符串

2016-11-28 00:00 477 查看
给一个字符串S和一个字符串数组T(T中的字符串要比S短许多),设计一个算法, 在字符串S中查找T中的字符串。

解答

字符串的多模式匹配问题。

我们把S称为目标串,T中的字符串称为模式串。设目标串S的长度为m,模式串的平均长度为 n,共有k个模式串。如果我们用KMP算法(或BM算法)去处理每个模式串, 判断模式串是否在目标串中出现, 匹配一个模式串和目标串的时间为O(m+n),所以总时间复杂度为:O(k(m+n))。 一般实际应用中,目标串往往是一段文本,一篇文章,甚至是一个基因库, 而模式串则是一些较短的字符串,也就是m一般要远大于n。 这时候如果我们要匹配的模式串非常多(即k非常大),那么我们使用上述算法就会非常慢。 这也是为什么KMP或BM一般只用于单模式匹配,而不用于多模式匹配。

那么有哪些算法可以解决多模式匹配问题呢?貌似还挺多的,Trie树,AC自动机,WM算法, 后缀树等等。我们先从简单的Trie树入手来解决这个问题。

Trie树,又称为字典树,单词查找树或前缀树,是一种用于快速检索的多叉树结构。 比如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树。

Trie树可以利用字符串的公共前缀来节约存储空间,这也是为什么它被叫前缀树。

如果我们有以下单词:abc, abcd, abd, b, bcd, efg, hig, 可以构造如下Trie树: (最右边的最后一条边少了一个字母)

回到我们的题目,现在要在字符串S中查找T中的字符串是否出现(或查找它们出现的位置), 这要怎么和Trie扯上关系呢?

假设字符串S = “abcd",那么它的所有后缀是:

abcd
bcd
cd
d

我们发现,如果一个串t是S的子串,那么t一定是S某个后缀的前缀。比如t = bc, 那么它是后缀bcd的前缀;又比如说t = c,那么它是后缀cd的前缀。

因此,我们只需要将字符串S的所有后缀构成一棵Trie树(后缀Trie), 然后查询模式串是否在该Trie树中出现即可。如果模式串t的长度为n, 那么我们从根结点向下匹配,可以用O(n)的时间得出t是否为S的子串。

下图是BANANAS的后缀Trie:

后缀Trie的查找效率很优秀,如果你要查找一个长度为n的字符串,只需要O(n)的时间, 比较次数就是字符串的长度,相当给力。 但是,构造字符串S的后缀Trie却需要O(m2 )的时间, (m为S的长度),及O(m2 )的空间。







package Hard;

import java.util.ArrayList;

import java.util.HashMap;

/**

* Given a string s and an array of smaller strings T, design a method to search s for each small string in T.

译文:

给一个字符串S和一个字符串数组T(T中的字符串要比S短许多),设计一个算法, 在字符串S中查找T中的字符串。

*

*/

public class S18_8 {

// 后缀树节点

static class SuffixTreeNode {

HashMap<Character, SuffixTreeNode> children = new HashMap<Character, SuffixTreeNode>();

char value;

ArrayList<Integer> indexes = new ArrayList<Integer>();

public SuffixTreeNode() {

}

public void insertString(String s, int index) {

indexes.add(index);

if (s != null && s.length() > 0) {

value = s.charAt(0);

SuffixTreeNode child = null;

if (children.containsKey(value)) {

child = children.get(value);

} else {

child = new SuffixTreeNode();

children.put(value, child);

}

String remainder = s.substring(1);

child.insertString(remainder, index);

}

}

public ArrayList<Integer> search(String s) {

if (s == null || s.length() == 0) {

return indexes;

} else {

char first = s.charAt(0);

if (children.containsKey(first)) {

String remainder = s.substring(1);

return children.get(first).search(remainder);

}

}

return null;

}

}

// 后缀树

static class SuffixTree {

SuffixTreeNode root = new SuffixTreeNode();

public SuffixTree(String s) {

for (int i = 0; i < s.length(); i++) {

String suffix = s.substring(i);

root.insertString(suffix, i);

}

}

public ArrayList<Integer> search(String s) {

return root.search(s);

}

}

public static void main(String[] args) {

String testString = "mississippi";

String[] stringList = { "is", "sip", "hi", "sis" };

SuffixTree tree = new SuffixTree(testString);

for (String s : stringList) {

ArrayList<Integer> list = tree.search(s);

if (list != null) {

System.out.println(s + ": " + list.toString());

}

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐