This question will require a suffix tree. Once you build the tree, you will traverse it in level order. Following code does the trick. It is in Java:
package org.algocode;
import java.util.ArrayDeque;
import java.util.Queue;
public class TernaryStringCombinator {
private static final int MAX_SIZE = 10000;
public static void main(String[] args) throws Exception {
// Read the input string.
String str = "abac";
if (str == null || str.length() > MAX_SIZE)
throw new Exception(
"Error! Input string is either null or greater than maximum allowed "
+ MAX_SIZE);
// Create the suffix tree
SuffixTree st = new SuffixTree(str);
st.levelOrderTraverse();
// You deduct one here because you don't want to count the root
// placeholder node.
System.out.println("Total nodes in tree " + (st.size() - 1));
// You deduct one here because you don't want to count the original
// string.
System.out.println("Total substrings with only one or two chars "
+ st.size2char());
}
/*
* A suffix tree is a tree of all possible contagious substrings of a
* string. It is a kind of a Trie. For example, for a given input string,
* ABBCD, the tree will store: ABBCD BBCD BCD CD D
*/
private static class SuffixTree {
private Node root;
private String s;
private int size;
private int size2char;
SuffixTree(String s_) throws Exception {
s = s_.toLowerCase();
size2char = s.length();
for (int i = 0; i < s.length(); i++)
insert(s.substring(i));
}
private void insert(String value) throws Exception {
if (root == null) {
root = new Node();
size++;
}
insert(root, value, 0);
}
// This is a recurrsive call to do the insertion
private void insert(Node node, String value, int index)
throws Exception {
Node next = null;
switch (value.charAt(index)) {
case 'a':
if (node.getA() == null)
createChildLink(node, value.charAt(index));
next = node.getA();
break;
case 'b':
if (node.getB() == null)
createChildLink(node, value.charAt(index));
next = node.getB();
break;
case 'c':
if (node.getC() == null)
createChildLink(node, value.charAt(index));
next = node.getC();
break;
default:
throw new Exception("Error! Character is not permitted. "
+ value);
}
if (index < (value.length() - 1)) {
insert(next, value.substring(index + 1), 0);
}
}
void levelOrderTraverse() {
if (root == null || root.isLeaf())
return;
Queue<Node> q = new ArrayDeque<Node>();
if (root.getA() != null)
q.add(root.getA());
if (root.getB() != null)
q.add(root.getB());
if (root.getC() != null)
q.add(root.getC());
while (!q.isEmpty()) {
Node n = (Node) q.poll();
// Only show if path has a color of 0 (two characters). Also the original
// string is not counted as a substring.
if (n.color() == 0) {
if (n.myPath().length() > 1 && !n.myPath().equalsIgnoreCase(s)) {
System.out.println("Two or more char path = "
+ n.myPath());
size2char++;
}
}
if (n.getA() != null)
q.add(n.getA());
if (n.getB() != null)
q.add(n.getB());
if (n.getC() != null)
q.add(n.getC());
}
}
Node root() {
return root;
}
int size() {
return size;
}
int size2char() {
return size2char;
}
private void createChildLink(Node parent, char childVal)
throws Exception {
Node child = new Node(parent, childVal);
size++;
switch (childVal) {
case 'a':
parent.setA(child);
break;
case 'b':
parent.setB(child);
break;
case 'c':
parent.setC(child);
break;
default:
throw new Exception("Error! Character is not permitted. "
+ childVal);
}
}
}
/*
* We will define an inner class to store a suffix tree node. Since it is a
* ternary string, we will have three child nodes of each string.
*/
private static class Node {
private Node parent;
private Node aLink;
private Node bLink;
private Node cLink;
private char value;
private String path;
// Color of a path. 0 if only one or two chars. 1 if all three are
// present in path.
private int color = 0;
Node(Node parent_, char value_) {
value = value_;
parent = parent_;
// Eagerly insert path
path = getPath();
// Eagerly calculate color. If my parent has a 1, then I will
// also be 1. If my parent has 0, then addition of myself can create
// my color to 0. This means that if my parent's path already has
// three
// characters, then I would be on a three character path.
if (parent.color() == 1)
color = 1;
else
colormyself();
}
Node() {
};
void setA(Node aLink_) {
this.aLink = aLink_;
}
void setB(Node bLink_) {
this.bLink = bLink_;
}
void setC(Node cLink_) {
this.cLink = cLink_;
}
Node getParent() {
return parent;
}
Node getA() {
return aLink;
}
Node getB() {
return bLink;
}
Node getC() {
return cLink;
}
char getValue() {
return value;
}
int color() {
return color;
}
// A special method to return combined string of parent
private String getPath() {
if (parent == null)
return null;
String path = parent.myPath();
if (path == null)
return String.valueOf(value);
StringBuilder stb = new StringBuilder();
stb.append(path);
stb.append(value);
return stb.toString();
}
String myPath() {
return path;
}
boolean isLeaf() {
return aLink == null && bLink == null && cLink == null;
}
private void colormyself() {
boolean sawA = false;
boolean sawB = false;
boolean sawC = false;
for (char c : path.toCharArray()) {
switch (c) {
case 'a':
sawA = true;
break;
case 'b':
sawB = true;
break;
case 'c':
sawC = true;
break;
}
if (sawA && sawB && sawC) {
color = 1;
break;
}
}
}
}
}