3

Imagine you have a special keyboard with all keys in a single row. The layout of characters on a keyboard is denoted by a string S1 of length 26. S1 is indexed from 0 to 25. Initially the index is at 0. To type a character we need to move the index of desired character. The time taken to move the index from i to j is |j-i| where || is absolute Value.

Given a string s1 and s2, describe the time taken to type string s2.

Given two string S1 and S2 where S1 = abcdeghijklmnopqrstuvwxyz

s2 = cba

and starting with index 0 ('A') find the time taken to type cba.

index of c = 2
count = 2 - 0
count = 2 + 1 (c to b )
count = 2 + 1(c to b) + 1 (b to a)
count returned should be 4.

I am sure this is easy to do in quadratic by running two loops but that is not an efficient solution. Any ideas how I can improve this?

Dhaval Shah
  • 618
  • 6
  • 15
  • Please clarify what is meant by "find cba"? Do you to determine for each char the index where it can be found in the first string? And when your first string simply contains the letters of the alphabet in their natural order, why do you think you need to compute anything? – GhostCat Apr 13 '19 at 04:46
  • 1
    I just updated the question again, please read it –  Apr 13 '19 at 04:50
  • is it `| j - 1 |` or `| j - i |` – AVX-42 Apr 13 '19 at 04:52
  • Okay, but then your tag is wrong. This question has nothing to do with Java. It basically asking about finding an efficient algorithm and to determine the complexity of that. – GhostCat Apr 13 '19 at 04:53
  • @Amiy | j - i| sorry typo –  Apr 13 '19 at 04:54

4 Answers4

2

Big Edit

Actually, by definition, the fact that S1 is a fixed length and does not depend on the input S2 means that @Amiy's answer is correct. Because indexOf runs only on S1, it will take at most a constant 26 steps. O(26n) = O(n). My answer is just a lower constant, running in 1n to 2n depending on the variation.


Another Edit

For the general case, where S1 is also a variable input and we can make no assumptions about it, see @user000001's hashing solution in O(nlogm).


Original Answers

If you rely on the fact that S1 is sorted and that each character is a value 1 off from its neighbours, you could just calculate the difference between the characters in S2 and sum it up

For example:
S2 = cba

Prepend a to S2 to get the starting value:
S2 = acba

For each character c1 and its right-neighbour c2 in S2,
distance += |c1 - c2|


If you don't rely on the fact that S1 is sorted (which is probably what you're looking for), you could index the values of S1 into an array and apply a similar algorithm as above:

For example:

S1 = "qwertyuiopasdfghjklzxcvbnm"
arr = array of size 26
let i = 0
for each character c in S1:
    arr[c - 'a'] = i++

Then, adapt the algorithm:

S2 = "cba"
let distance = 0
for each character c1 and its right-neighbour c2 in S2:
    distance += |arr[c1 - 'a'] - arr[c2 - 'a']|


Both algorithms solve the problem in O(n), whereas the first one uses O(1) space and the second uses O(n) space.
Yidna
  • 452
  • 4
  • 12
  • I don't think OP considers s1 fixed length, because he mentions the "obvious" quadratic solution – user000001 Apr 13 '19 at 11:49
  • Yeah, I was trying to figure out which was more likely: that OP meant `S1` was fixed length, or that OP quadratic'd wrong. I feel that when most people see nested loops they immediately think quadratic without considering that one of them might be constant, so I went with the former. Nevertheless, I shall edit my response again for the sake of completeness! – Yidna Apr 13 '19 at 12:13
1

One way to reduce the complexity from O(N^2) to O(Nlog(N), would be to create a HashMap out of the String.

Something like this:

String s1 = "abcdeghijklmnopqrstuvwxyz";
String s2 = "cba";

Map<Byte, Integer> map = new HashMap<>();

int i = 0;
for (Byte b: s1.getBytes()) {
    map.put(b, i++);
}

int result = 0;
int previndex = 0;
for (Byte b: s2.getBytes()) {
    int currentindex = map.get(b);
    result += (int) Math.abs(currentindex - previndex);
    previndex = currentindex;
}

System.out.println("resutlt= " + result);
user000001
  • 32,226
  • 12
  • 81
  • 108
  • Why is the complexity `O(Nlog(N))` ? – nice_dev Apr 13 '19 at 05:42
  • @vivek_23: I assume log(N) complexity for `map.get` and `map.put` method, which is the worst case (on recent Java implementations) according to https://stackoverflow.com/a/4553642/828193 – user000001 Apr 13 '19 at 05:43
  • There ain't any collisions I presume, so it would be O(1). Why not make a `byte[] hash = new byte[26]` since `s1` is always going to be of length 26? – nice_dev Apr 13 '19 at 05:48
  • @vivek_23: True, you could do that. But in general `s1` could contain unicode, not sure how well it would work considering unicode characters as indexes. – user000001 Apr 13 '19 at 05:52
  • Ok, `hashmap` is a better option for unicode but it would still contain unique characters I presume from OP's question(but not very sure though). So, collisions are very less likely to occur. The question would have been more interesting with duplicate characters. – nice_dev Apr 13 '19 at 06:03
1

This one is more simpler to understand:

String s1 = "abcdefghijklmnopqrstuvwxyz";
String s2 = "cba";
int jumps = 0;

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

        if(i==0){
            jumps += s1.indexOf(s2.charAt(i));
        }else{
            jumps += Math.abs(s1.indexOf(s2.charAt(i)) - s1.indexOf(s2.charAt(i-1)));
        }
}
System.out.println("Total Jumps: "+jumps);

Here, it looks for each character of s2 in s1 with a loop. First time the if block will run and then else for the remaining characters. For every character in s2, it will keep adding the jumps/hops/time-taken to the total and will return you after the loop is done.

Also, it counts the jumps by subtracting the char-position of where we are from the char-position of where we were. In simple terms, since it starts with position 0, you can count like this: [(C-0)+(B-C)+(B-A)].

kyj
  • 11
  • 3
  • Welcome to Stack Overflow! Please add some explanation to your answer, that's an essential part of answers here on Stack Overflow. This guide [How to write a good answer](https://stackoverflow.com/help/how-to-answer) might be helpful for you. Thanks! – David Apr 13 '19 at 07:08
0

Here is the algorithm in Java:

String s1 = "abcdefghijklmnopqrstuvwxyz";
String s2 = "cba";

int pos = 0;
int time = 0;

for (int ind = 0;ind < s2.length(); ind++) {
    char cur_char = s2.charAt(ind);
    index = s1.indexOf(cur_char);
    time += Math.abs(index - pos);
    pos = index;
}

System.out.println("Time taken: " + time);

For above values output is: Time taken: 4

Edit:
Time complexity of this algorithm is O(n)
Since length of s1 is constant, let k.
And length of string s2 ia variable, let n.
Time complexity: O(nk) = O(n) [Since k is a constant value]

AVX-42
  • 755
  • 2
  • 13
  • 21
  • This looks like O(N), but it is really O(N^2), due to the `indexOf` method call in the loop. https://stackoverflow.com/a/3562992/828193 – user000001 Apr 13 '19 at 05:38
  • Just mentioned in my answer that this is technically in `O(n)` time since `s1` is a constant length – Yidna Apr 13 '19 at 06:22