interviewstreet.com - String similarity - java

I'm trying to solve the string similarity question on interviewstreet.com. My code is working for 7/10 cases (and it is exceeding the time limit for the other 3).
Here's my code -
public class Solution {
public static void main(String[] args) {
Scanner user_input = new Scanner(System.in);
String v1 = user_input.next();
int number_cases = Integer.parseInt(v1);
String[] cases = new String[number_cases];
for(int i=0;i<number_cases;i++)
cases[i] = user_input.next();
for(int k=0;k<number_cases;k++){
int similarity = solve(cases[k]);
System.out.println(similarity);
}
}
static int solve(String sample){
int len=sample.length();
int sim=0;
for(int i=0;i<len;i++){
for(int j=i;j<len;j++){
if(sample.charAt(j-i)==sample.charAt(j))
sim++;
else
break;
}
}
return sim;
}
}
Here's the question -
For two strings A and B, we define the similarity of the strings to be the length of the longest prefix common to both strings. For example, the similarity of strings "abc" and "abd" is 2, while the similarity of strings "aaa" and "aaab" is 3.
Calculate the sum of similarities of a string S with each of it's suffixes.
Input:
The first line contains the number of test cases T. Each of the next T lines contains a string each.
Output:
Output T lines containing the answer for the corresponding test case.
Constraints:
1 <= T <= 10
The length of each string is at most 100000 and contains only lower case characters.
Sample Input:
2
ababaa
aa
Sample Output:
11
3
Explanation:
For the first case, the suffixes of the string are "ababaa", "babaa", "abaa", "baa", "aa" and "a". The similarities of each of these strings with the string "ababaa" are 6,0,3,0,1,1 respectively. Thus the answer is 6 + 0 + 3 + 0 + 1 + 1 = 11.
For the second case, the answer is 2 + 1 = 3.
How can I improve the running speed of the code. It becomes harder since the website does not provide a list of test cases it uses.

I used char[] instead of strings. It reduced the running time from 5.3 seconds to 4.7 seconds and for the test cases and it worked. Here's the code -
static int solve(String sample){
int len=sample.length();
char[] letters = sample.toCharArray();
int sim=0;
for(int i=0;i<len;i++){
for(int j=i;j<len;j++){
if(letters[j-i]==letters[j])
sim++;
else
break;
}
}
return sim;
}

used a different algorithm. run a loop for n times where n is equals to length the main string. for each loop generate all the suffix of the string starting for ith string and match it with the second string. when you find unmatched character break the loop add j's value to counter integer c.
import java.io.BufferedReader;
import java.io.InputStreamReader;
class Solution {
public static void main(String args[]) throws Exception {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int T = Integer.parseInt(in.readLine());
for (int i = 0; i < T; i++) {
String line = in.readLine();
System.out.println(count(line));
}
}
private static int count(String input) {
int c = 0, j;
char[] array = input.toCharArray();
int n = array.length;
for (int i = 0; i < n; i++) {
for (j = 0; j < n - i && i + j < n; j++)
if (array[i + j] != array[j])
break;
c+=j;
}
return c;
}
}

I spent some time to resolve this question, and here is an example of my code (it works for me, and pass thru all the test-cases):
static long stringSimilarity(String a) {
int len=a.length();
char[] letters = a.toCharArray();
char localChar = letters[0];
long sim=0;
int sameCharsRow = 0;
boolean isFirstTime = true;
for(int i=0;i<len;i++){
if (localChar == letters[i]) {
for(int j = i + sameCharsRow;j<len;j++){
if (isFirstTime && letters[j] == localChar) {
sameCharsRow++;
} else {
isFirstTime = false;
}
if(letters[j-i]==letters[j])
sim++;
else
break;
}
if (sameCharsRow > 0) {
sameCharsRow--;
sim += sameCharsRow;
}
isFirstTime = true;
}
}
return sim;
}
The point is that we need to speed up strings with the same content, and then we will have better performance with test cases 10 and 11.

Initialize sim with the length of the sample string and start the outer loop with 1 because we now in advance that the comparison of the sample string with itself will add its own length value to the result.

import java.util.Scanner;
public class StringSimilarity
{
public static void main(String args[])
{
Scanner user_input = new Scanner(System.in);
int count = Integer.parseInt(user_input.next());
char[] nextLine = user_input.next().toCharArray();
try
{
while(nextLine!= null )
{
int length = nextLine.length;
int suffixCount =length;
for(int i=1;i<length;i++)
{
int j =0;
int k=i;
for(;k<length && nextLine[k++] == nextLine[j++]; suffixCount++);
}
System.out.println(suffixCount);
if(--count < 0)
{
System.exit(0);
}
nextLine = user_input.next().toCharArray();
}
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

Related

Palindrome in java

Here what I tried
sample input is "aabaa"
eg: in if condition val[0] = a[4]
if it is equal i stored it in counter variable if it is half of the length it original string it is palindrome
if it is not it is not a palindrome
I tried with my basic knowledge in java if there is any errors let me know
boolean solution(String inputString) {
int val = inputString.length();
int count = 0;
for (int i = 0; i<inputString.length(); i++) {
if(inputString.charAt(i) == inputString.charAt(val-i)) {
count = count++;
if (count>0) {
return true;
}
}
}
return true;
}
How about
public boolean isPalindrome(String text) {
String clean = text.replaceAll("\\s+", "").toLowerCase();
int length = clean.length();
int forward = 0;
int backward = length - 1;
while (backward > forward) {
char forwardChar = clean.charAt(forward++);
char backwardChar = clean.charAt(backward--);
if (forwardChar != backwardChar)
return false;
}
return true;
}
From here
In your version you compare first element with last, second with second last etc.
last element in this case is inputString.length()-1(so need to use 'inputString.charAt(val-i-1)' . If you iterate till end, then the count should be equal to length of the string.
for(int i = 0; i<inputString.length(); i++){
if(inputString.charAt(i) == inputString.charAt(val-i-1)){
count ++;
}
}
return (count==val); //true when count=val
Or alternatlively iterate till the mid point of the array, then count value is val/2.
for(int i = 0; i<inputString.length()/2; i++){
if(inputString.charAt(i) == inputString.charAt(val-i-1)){
count ++;
}
}
return (count==val/2); //true when count=val/2
There's no constraints in the question so let me throw in a more cheesy solution.
boolean isPalindrome(String in)
final String inl = in.toLowerCase();
return new StringBuilder(inl).reverse().toString().equals(inl);
}
A palindrome is a word, sentence, verse, or even a number that reads the same forward and backward. In this java solution, we’ll see how to figure out whether the number or the string is palindrome in nature or not.
Method - 1
class Main {
public static void main(String[] args) {
String str = "Nitin", revStr = "";
int strLen = str.length();
for (int i = (strLen - 1); i >=0; --i) {
revStr = revStr + str.charAt(i);
}
if (str.toLowerCase().equals(revStr.toLowerCase())) {
System.out.println(str + " is a Palindrome String.");
}
else {
System.out.println(str + " is not a Palindrome String.");
}
Method - 2
class Main {
public static void main(String[] args) {
int n = 3553, revNum = 0, rem;
// store the number to the original number
int orgNum = n;
/* get the reverse of original number
store it in variable */
while (n != 0) {
remainder = n % 10;
revNum = revNum * 10 + rem;
n /= 10;
}
// check if reversed number and original number are equal
if (orgNum == revNum) {
System.out.println(orgNum + " is Palindrome.");
}
else {
System.out.println(orgNum + " is not Palindrome.");
}

Number of substrings of string which has three vowels

I participated in a coding challenge on hackerearth , and i was asked the following question .
Alice and Bob are playing a game in which Bob gives a string SS of length NN consisting of lowercase English alphabets to Alice and ask her to calculate the number of sub-strings of this string which contains exactly 3 vowels.
This is my code
import java.io.BufferedReader;
import java.io.InputStreamReader;
class TestClass1{
public static void main(String args[] ) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = br.readLine();
int N = Integer.parseInt(line);
String stringArray[]=new String[N];
for (int i = 0; i < N; i++) {
int len = Integer.parseInt(br.readLine());
stringArray[i]=br.readLine();
}
for (int i = 0; i < N; i++) {
System.out.println(determineNumberOfSubstring(stringArray[i]));
}
}
public static int determineNumberOfSubstring(String str)
{
int numberOfSubstring=0;
for(int i=0;i<str.length();i++)
{
int ctr=0;
for(int j=1;j<str.length()-i;j++)
{
String subString = str.substring(i,i+j);
if(subString.length()<3)
{
continue;
}
if(subString.contains("a")||subString.contains("e")||subString.contains("i")||subString.contains("o")||subString.contains("u")
||subString.contains("A")||subString.contains("E")||subString.contains("I")||subString.contains("O")||subString.contains("U"))
{
ctr+=3;
}
}
if(ctr==3){
numberOfSubstring++;
}
}
return numberOfSubstring;
}
}
Iam getting time limit exceeded for the above code . Could any one help me out on how to optimise it .
Update1
Code as per #GhostCat logic , this needs to be tested and is not the final code.
class TestClass1{
public static void main(String args[] ) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = br.readLine();
int N = Integer.parseInt(line);
String stringArray[]=new String[N];
for (int i = 0; i < N; i++) {
int len = Integer.parseInt(br.readLine());
stringArray[i]=br.readLine();
}
for (int i = 0; i < N; i++) {
System.out.println(determineNumberOfSubstring(stringArray[i]));
}
}
public static int determineNumberOfSubstring(String str)
{
int numberOfSubstring=0;
int ctr=0;
int subStringStart=0;
Stack<String> s = new Stack<String>();
for(int i=0;i<str.length();i++)
{
if(isVowel(str.charAt(i)))
ctr++;
if(ctr==3)
{
numberOfSubstring++;
ctr=0;
if(s.empty())
s.push(str.substring(0,i));
else
s.push(new String(s.peek().substring(1,i+1)));
i=str.indexOf(s.peek().charAt(1))-1;
}
}
return numberOfSubstring;
}
private static boolean isVowel(char c) {
if(c=='a'||c=='e'||c=='i'||c=='o'||c=='u'
||c=='A'||c=='E'||c=='I'||c=='O'||c=='U')
return true;
return false;
}
}
Hint: your code is iterating the whole substring for each and any lower and upper case vowel there is. And that happens in a loop in a loop.
Instead: use ONE loop that goes over the characters of the input string. And then check each position if it is a vowel by checking against a set of characters (containing those valid vowels). The final thing you need: a simple "sliding" window; like: when you spot three vowels, you can increase your counter; to then "forget" about the first of the three vowels you just found; like in:
a x e y i --> vowels a/e/i give one substring
x e y i ... and you look out for the next substring e/i/... now
Actual implementation is left as exercise to the reader ...
Long story short: this count can be computed by processing your input ONCE. Your code is iterating countless times more than once. Well, not countless, but N * N * some more.
( the one thing to be careful with: when using a Set<Character> be precise when you turn a char value into aCharacter object; you want to minimize the instances of that happening, too; as that is a rather expensive operation )
HAPPY CODING ###### (if useful then upvote)
Que: count possible substring contain exactly 3 vowel in given string
my approach in O(n):
#include<bits/stdc++.h>
using namespace std;
int main()
{
string s;
cin>>s;
vector<long long int>idex;
idex.push_back(-1);
for(int i=0;i<n;i++)
{
if(s[i]=='a' || s[i]=='e' || s[i]=='i' || s[i]=='o' || s[i]=='u')
{
idex.push_back(i);
}
}
idex.push_back(n);
if(idex.size()<5)
{
cout<<0<<endl;
}
else
{
long long int ans=0;
for(int i=1;i<=idex.size()-4;i++)
{
ans+=(idex[i]-idex[i-1])*(idex[i+3]-idex[i+2]);
}
cout<<ans<<endl;
}
}

Java Loop - Removing numbers that has 4 or more of the same digits

I was wondering how one would create a loop where it would print all the numbers from 200000 - 900000 and remove all the digits such as 222244.
So far this is all I have:
import java.io.FileNotFoundException;
import java.io.PrintWriter;
public class alg {
public void algorithm(){
}
public static void main(String []args){
PrintWriter file = null;
try {
file = new PrintWriter("output.txt");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int x;
for (x = 200000; x < 900000; x++){
file.println(x);
}
}
file.close();
}
// return true if integer has 4 or more of same digit, otherwise false
static boolean check4(int i) {
int[] charCounts = new int[10];
for (char c : String.valueOf(i).toCharArray()) {
if (++charCounts[c-'0'] == 4) return true;
}
return false;
}
Since there are no mathematical solutions for this, you need to parse the numbers:
int x;
// loop through all the numbers
for (x = 200000; x < 900000; x++) {
// for each number x, check digits 0-9
for (int i = 0; i < 10; i ++) {
// convert current number x to a String
final String originalNumber = Integer.toString(x);
// replace all digits i in current number x and check if the lenth was decreased by 4 or more
if (originalNumber.replaceAll(Integer.toString(i)).length() <= originalNumber.length() - 4) {
// if so, print the current number x to your file
file.println(x);
}
}
}
I think there are better solutions but it should work.
I would suggest using java Pattern class with some kind of pattern like:
[0-9]{4}[0-9]{2}.
In loop you could just call String stringified = yourNumber + "";
and that match it with ALREADY compiled pattern.
Here's how I would do it. It is essentially a method to stringify the integer, break it into a Character[] and uses the Collections.frequency() method to check your condition. It will return true if the number has 4 or more of the same digit.
private static boolean containsFourOfSameDigit(Integer x) {
final int threshold = 4; //Change for 3, 2, whatever you want.
String s = x.toString();
/* Populate a char array and then list with all digits */
Character[] ch = new Character[s.length()];
for(int i = 0; i < s.length(); i++)
ch[i] = s.charAt(i);
ArrayList<Character> allDigits = new ArrayList<Character>(Arrays.asList(ch));
HashSet<Character> uniqueDigits = new HashSet<Character>(allDigits);
for(Character c : uniqueDigits)
if(Collections.frequency(allDigits, c) >= threshold)
return true;
return false;
}
System.out.println(containsFourOfSameDigit(200000)); will print true, as will your 222244 example. Note this doesn't complete your program, it is just the boolean method you are seeking.

Reduce the value of a letter, e.g. can change 'd' to 'c', but cannot change 'c' to 'd'. In order to form a palindrome

public class Solution {
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int tc = Integer.parseInt(br.readLine());//I get Numberformat Exception here
for(int i=0;i<tc;i++) // Even if my inputs are on separate lines
{
String original = br.readLine();
palindrome(original);
}
}
public static void palindrome(String original)
{
String reverse="";
int length = original.length();
for ( int i = length - 1 ; i >= 0 ; i-- )
reverse = reverse + original.charAt(i);
if (original.equals(reverse))
{
System.out.println(0);
}
else
{
char[] org = original.toCharArray();
int len = org.length;
int mid = len / 2;
if(len % 2 == 0)
{
char[] front = new char[mid];
char[] back = new char[mid];
for(int i=0;i<mid;i++)
{
front[i] = org[i];
}
int j=0;
for(int i=len-1;i>=mid;i--)
{
back[j] = org[i];
j++;
while(j > mid)
{
break;
}
}
change(front,back,mid);
}
else
{
char[] front = new char[mid];
char[] back = new char[mid];
for(int i=0;i<mid;i++)
{
front[i] = org[i];
}
int j=0;
for(int i=len-1;i>mid;i--)
{
back[j] = org[i];
j++;
while(j > mid)
{
break;
}
}
change(front,back,mid);
}
}
}
public static void change(char[] front,char[] back,int len)
{
int count =0;
for(int i =0;i<len;i++)
{
if(front[i] != back[i] )
{
count += (back[i] - front[i]);
}
}
System.out.println(count)
}
}
What i try to do here is get an input from the number of test cases say 3 in my first line followed by the test-cases themselves.
sample input :
3
abc
abcba
abcd
Now it has to check if the string is a palindrome if its so it ll print 0
else it breaks the string into two halves front and back and finds the minimum number of changes to make it a palidrome.
here i have also checked if its a odd or even length string if odd i have omitted the middle char.
By changes we can only change 'd' to 'b' not 'b' to 'd'
Once a letter has been changed to 'a', it can no longer be changed.
My code works fine for the above input but it doesnt for some other inputs i dont quiet understand why..
for instance if i give a custom test case as
5
assfsdgrgregedhthtjh
efasfhnethiaoesdfgv
ehadfghsdfhmkfpg
wsertete
agdsjgtukgtulhgfd
I get a Number Format Exception.
Your code works fine here, whithout NumberFormatException: http://ideone.com/QJqjmG
This may not solve your problem, but improves your code...
As first user input you are expecting an integer. You are parsing the String returned by br.readLine() and do not take care of the NumberFormatException parseInt(...) may throw.
Just imagine someone hits space or return key as first input.
So I propose to put a try-catch-block around the parseInt(...). Here is an example how this may look like.
Guys thank you for all your suggestion i just found out why my other test cases weren't working
public static void change(char[] front,char[] back,int len)
{
int count =0;
for(int i =0;i<len;i++)
{
if(front[i] != back[i] )
{
count += (back[i] - front[i]);
}
}
System.out.println(count)
}
This part of my code has to be changed to
public static void change(char[] front,char[] back,int len)
{
int count =0;
for(int i =0;i<len;i++)
{
if(front[i] != back[i] )
{
char great = findGreatest(front[i],back[i]);
if(great == back[i])
{
count += (back[i] - front[i]);
}
else
{
count += (front[i] - back[i]);
}
}
}
System.out.println(count);
}
public static char findGreatest(char first,char second)
{
int great = first;
if(first < second)
{
great = second;
}
return (char)great;
}
Because i get negative values coz of subtracting ascii's which are greater than them and as i have already mentioned i can only do 'd' to 'a' not the other way round.
Thank you for your time guys!

Sequence Count i.e suppose aabbbaaccd is string output should be a=2,b=3,a=2,c=2,d=1

As i have tried ,it gives ArrayIndexOutOfBounds Ecxeption and does not print last char
please help me to find bug in my code.or is there any alternate
public static void sequenceCount(String s) {
int counter;
int i=0;
char c;
char[] arr = s.toCharArray();
while(i<arr.length){
counter=0;
c = arr[i];
while(c==arr[i]){
counter++;
i++;
}
System.out.println("letter"+" "+c+":"+"number of times"+counter);
}
}
As i am a novice to java my code may be inefficient
Your inner loop is not bound by the length of the array. Try:
while(i < arr.length && c==arr[i]){
counter++;
i++;
}
This works - you need to ensure that your inner loop doesn't pass beyond the end of the string, and you need to always catch the last letter, too:
public static void sequenceCount(String s) {
char[] arr = s.toCharArray();
int i = 0, n = arr.length;
while (i < n) {
char c = arr[i];
int count = 0;
do {
++i; ++count;
} while (i < n && arr[i] == c);
System.out.println("letter "+ c +":"+"number of times " + count);
}
}
My approach would be to use two for loops.
The first for loop would run a loop with the decimal equivalent of A to Z.
The second for loop would run a loop that runs through the entire character array/string (I'd prefer a string rather than a char array here) and check to see if that given value at that index is equal the the value ran by the first for loop. If they are equal than add one to count. Print.
Don't forget to reset your counter after every run as well.
Similar Topic can be found here: Counting letters in a string using two for-loops
While many answers here are O(n^2), I tried to do it within O(n) time using recursion. This is modified from existing code that I already had so I know the method returns an int, but I don't use it (it's left over from copied code - fix it as you see fit)
public class CountCharSeqRecursive {
private String test = "AAABBA"; // (3)A(2)B(1)A
private StringBuilder runningString = new StringBuilder();
public static void main(String[] args) {
CountCharSeqRecursive t = new CountCharSeqRecursive();
System.out.println(t.getEncryptedValue(t.test));
}
public String getEncryptedValue(String seq){
int startIndex=0;
this.createCounterSeq(seq.charAt(startIndex), seq, startIndex);
return runningString.toString();
}
private int createCounterSeq(char prev, String sequence, int currentIndex){
return createCounterSeq(prev, sequence, currentIndex, 0);
}
private int createCounterSeq(char prev, String sequence, int currentIndex, int count){
if(currentIndex<sequence.length()){
char current = sequence.charAt(currentIndex);
if((prev^current) < 1){
++count;
}else {
this.addToSequence(count, prev);
count = 1;
}
return count += createCounterSeq(current, sequence, ++currentIndex, count);
}
this.addToSequence(count, prev);
return count;
}
private void addToSequence(int count, char ch){
runningString.append("("+count+")").append(ch);
}
}
My solution using HashSet, Works for all cases of a non-empty string.
public static void main(String[] args) {
// TODO Auto-generated method stub
HashSet<Character> set = new HashSet<Character>();
String input = "aabbcdeaab";
set.add(input.charAt(0));
int count = 1;
StringBuilder output = new StringBuilder("");
for(int i=1;i<input.length();i++) {
char next = input.charAt(i);
if(set.contains(next)) {
count++;
}else {
char prev = input.charAt(i-1);
output.append(Character.toString(prev) + count );
set.remove(prev);
set.add(next);
count=1;
}
}
output.append(Character.toString(input.charAt(input.length()-1)) + count );
System.out.println(output.toString());
}

Categories

Resources