让我们尝试一次计算多个密码子,而不是仅仅计算 TAG 的实例。
public static final void main( String[] args )
{
String input = "TACACTAGATCGCACTGCTAGTATC";
if (args.length > 0) {
input = args[0].trim();
}
System.out.println(input);
HashMap<Character, Node> searchPatterns = createCodons();
findCounts(input, searchPatterns);
printCounts(searchPatterns);
}
这个解决方案使用树来存储我们感兴趣的字符序列。树中从根到叶的每条路径代表一个可能的序列。我们将创建四棵树;以 T、A、C 和 G 开头的密码子。我们将这些树存储在 HashMap 中,以便通过它们的起始字符检索。
/**
Create a set of sequences we are interesting in finding (subset of
possible codons). We could specify any pattern we want here.
*/
public static final HashMap<Character, Node> createCodons()
{
HashMap<Character, Node> codons = new HashMap<Character,Node>();
Node sequencesOfT = new Node('T'); // T
Node nodeA = sequencesOfT.addChild('A'); // /
nodeA.addChild('C'); // A
nodeA.addChild('G'); // / \
codons.put('T', sequencesOfT); // C G
Node sequencesOfA = new Node('A'); // A
Node nodeT = sequencesOfA.addChild('T'); // /
nodeT.addChild('C'); // T
nodeT.addChild('G');; // / \
codons.put('A', sequencesOfA); // C G
Node sequencesOfC = new Node('C'); // C
Node nodeG = sequencesOfC.addChild('G'); // /
nodeG.addChild('T'); // G
nodeG.addChild('A'); // / \
codons.put('C', sequencesOfC); // T A
Node sequencesOfG = new Node('G'); // G
Node nodeC = sequencesOfG.addChild('C'); // /
nodeC.addChild('T'); // C
nodeC.addChild('A'); // / \
codons.put('G', sequencesOfG); // T A
return codons;
}
这是我们的 Node 类的样子。
public class Node
{
public char data; // the name of the node; A,C,G,T
public int count = 0; // we'll keep a count of occurrences here
public Node parent = null;
public List<Node> children;
public Node( char data )
{
this.data = data;
children = new ArrayList<Node>();
}
public Node addChild( char data )
{
Node node = new Node(data);
node.parent = this;
return (children.add(node) ? node : null);
}
public Node getChild( int index )
{
return children.get(index);
}
public int hasChild( char data )
{
int index = -1;
int numChildren = children.size();
for (int i=0; i<numChildren; i++)
{
Node child = children.get(i);
if (child.data == data)
{
index = i;
break;
}
}
return index;
}
}
为了计算出现次数,我们将迭代输入的每个字符,并为每次迭代检索我们感兴趣的树(A、G、C 或 T)。然后我们尝试沿着树向下走(从根到叶)使用输入的后续字符 - 当我们无法在节点的子节点列表中找到输入的下一个字符时,我们停止遍历。此时,我们增加该节点上的计数,以指示找到以该节点结尾的字符序列。
public static final void findCounts(String input, HashMap<Character,Node> sequences)
{
int n = input.length();
for (int i=0; i<n; i++)
{
char root = input.charAt(i);
Node sequence = sequences.get(root);
int j = -1;
int c = 1;
while (((i+c) < n) &&
((j = sequence.hasChild(input.charAt(i+c))) != -1))
{
sequence = sequence.getChild(j);
c++;
}
sequence.count++;
}
}
为了打印结果,我们将遍历每棵树从根到叶子,在遇到节点时打印节点,并在到达叶子时打印计数。
public static final void printCounts( HashMap<Character,Node> sequences )
{
for (Node sequence : sequences.values())
{
printCounts(sequence, "");
}
}
public static final void printCounts( Node sequence, String output )
{
output = output + sequence.data;
if (sequence.children.isEmpty())
{
System.out.println(output + ": " + sequence.count);
return;
}
for (int i=0; i<sequence.children.size(); i++)
{
printCounts( sequence.children.get(i), output );
}
}
这是一些示例输出:
TAGAAAAGGGAAAGATAGT
TAC: 0
TAG: 2
GCT: 0
GCA: 0
ATC: 0
ATG: 0
CGT: 0
CGA: 0
TAGCGTATC
TAC: 0
TAG: 1
GCT: 0
GCA: 0
ATC: 1
ATG: 0
CGT: 1
CGA: 0
从这里我们可以轻松地扩展解决方案,以保留找到每个序列的位置列表,或记录与输入相关的其他信息。这个实现有点粗糙,但希望这能让您深入了解解决问题的其他方法。