-2

One question for an Interview was that : given a ternary string , find the number of contiguous substrings which contain only one or two character of the given ternary string. A ternary string is one which is made up of at most 3 characters. Like for ex: bcabb is a ternary string over the set {a,b,c}. Ans to the above example will be: b,c,a,b,b,bc,ca,ab,bb ie.,9.

Note: substrings are decided by there start and end index not uniqueness.

Can anyone tell me what algo to follow in this question.

nishantsatyam
  • 27
  • 1
  • 2
  • 8
  • one thing more time complexity shud be O(n) as string can be of length 100000. – nishantsatyam Oct 03 '12 at 13:46
  • your answer should be **8** and not **9** because the `b` string is duplicated in the answer. isn't? – MOHAMED Oct 03 '12 at 14:13
  • @MohamedKALLEL in addition to the duplication of `b`, there's also `abb`, which isn't listed, so the proper solution isn't completely clear, as you note... – twalberg Oct 03 '12 at 14:40
  • @MohamedKALLEL:correct bro missed that one...but first one is incorrect 'cause b,b have two different indexes...ans will remain 9. – nishantsatyam Oct 03 '12 at 16:21
  • Guys I am adding another example to clarify:suppose string is "abbaccb", for this number of substrings are 17.The list is : a,b,b,a,c,c,b,abba,ab,abb,ba,bba,ac,acc,cc,ccb,cb. – nishantsatyam Oct 03 '12 at 16:47
  • This confuses me more. Why there is 17 substrings, on what basis these strings were selected ? – phoxis Oct 03 '12 at 17:22
  • I have mentioned they are on the basis of indexes.The mentioned substrings are contiguous strings possible with one or two character from {a,b,c}. – nishantsatyam Oct 03 '12 at 18:47

6 Answers6

1

I couldn't fully understand what you are saying, but from the example and the description i think the answer will be 2 * strlen (string) - 1 . This is because you have strlen (string) number of single length string, and strlen (string) number of 2 length substring from the given string.

phoxis
  • 60,131
  • 14
  • 81
  • 117
1

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;
            }

        }

    }

}

}

0

assume that the length of your string is n

So the result will be : 2*n-1

MOHAMED
  • 41,599
  • 58
  • 163
  • 268
0

Iterate over the input string, considering the characters one by one. At each iteration, have 10 counters that count different types of substrings. Suppose you are considering a position p; then the different types of substrings are:

  1. Those that end before position p; i call it result, because when the algorithm stops, it will contain the answer
  2. Those that end at position p or later, and contain only 'a' characters; called aa
  3. Those that end at position p or later, and contain only 'a' and 'b' characters; called ab
  4. Similar to the above; called ac
  5. Similar to the above; called ba
  6. Similar to the above; called bb
  7. Similar to the above; called bc
  8. Similar to the above; called ca
  9. Similar to the above; called cb
  10. Similar to the above; called cc

It is easy to update each counter from position p to p+1, for example, if the character at position p is 'a', then:

  • Add all the counters to result, to account for all substrings that end at p
  • It's possible to continue a streak 'aaaaa' to 'aaaaaa', so increase the counter aa
  • It's possible to append 'a' to any substring of type 'ba'
  • It's possible to append 'a' to any substring of type 'ab', making it into 'ba', so add the counter ab to ba
  • It's possible to append 'a' to any substring of type 'b', making it into 'ba', so add the counter b to ba
  • It's impossible to append 'a' to any other substrings, so set all other counters to zero

Code fragment:

...
int result = 0;
int counters[3][3] = {{0}}; // [0][0] is aa; [0][1] is ab, etc
int L, x, y; // L represents the current Letter; x and y represent other letters
int p; // position in input string

for (p = 0; ; p++)
{
    for (y = 0; y < 3; y++)
        for (x = 0; x < 3; x++)
            result += counters[y][x];
    switch (str[p])
    {
    case 'a': L = 0; x = 1; y = 2; break;
    case 'b': L = 1; x = 2; y = 0; break;
    case 'c': L = 2; x = 0; y = 1; break;
    case '\0': return result;
    default: abort();
    }

    counters[L][L] += 1;
    counters[x][L] += counters[x][x] + counters[L][x];
    counters[y][L] += counters[y][y] + counters[L][y];
    counters[L][x] = 0;
    counters[x][x] = 0;
    counters[y][x] = 0;
    counters[L][y] = 0;
    counters[x][y] = 0;
    counters[y][y] = 0;
}

Oddly enough, this code outputs 10 (instead of 9) for string bcabb and 18 (instead of 17) for string abbaccb. I leave it as a fun exercise for you to discover which substring is missing...

anatolyg
  • 26,506
  • 9
  • 60
  • 134
0
static void ternaryStringSubstring(String A){
        System.out.println("input ternary string :"+A);
        int output = 0;

        for(int i=0;i<A.length();i++){
            String newStr = "";
            for(int j=i+1;j<=A.length();j++){
                newStr = A.substring(i, j);
                boolean isTernary = true;// Keep track of long loop
                List<String> l = new ArrayList<String>();
                for(int k=0;k<newStr.length();k++){
                    String sew = newStr.substring(k, k+1);
                    if(!l.contains(sew)){
                        l.add(sew);
                    }
                    if(l.size()>2){
                        isTernary = false;//In case its a long string, it would break
                        break;
                    }
                }
                if(isTernary){
                    output = output+1;
                }


            }

        }

        System.out.println("output :"+output);

    }

Let me know for more optimized solution

Cool Coder
  • 309
  • 2
  • 11
  • you are using length of string in for loop which can be as given in question statement be 100000. Time complexity will go high, any way solution required in O(n) time. – nishantsatyam Oct 17 '12 at 10:04
0

First, as I can see that you are printing duplicate elements. I am assuming that the size of string is 'n'. So you have to print n elements no matter if they are repeated. Same hold for two consecutive elements, so take 'n-1' for that. The total till now is '2n-1'. Now search for 2 consecutive elements and add "+2" for that in total (provided the preceding and succeeding characters are not null). Now do search for 3 consecutive elements and add "+3" for that in total. Repeat this till 'n' as there can be 'n' duplicate characters. Add them all. Hope this helps.

Gaurav
  • 1,005
  • 3
  • 14
  • 33