This Question is part of ongoing Competition , I have solved the 75% of this Question Data Set but the 25% is giving me TLE. I am asking why it's is giving TLE an i am sure my complexity is O(n*n)Question:
String S consisting of N lowercase English alphabets. We has prepared a list L consisting of all non empty substrings of the string S.
Now he asks you Q questions. To ith question, you need to count the number of ways to choose exactly Ki equal strings from the list L
For Example:
String = ababa
L = {"a", "b", "a", "b", "a", "ab", "ba", "ab", "ba", "aba", "bab", "aba", "abab", "baba", "ababa"}.
k1 = 2: There are seven ways to choose two equal strings ("a", "a"), ("a", "a"), ("a", "a"), ("b", "b"), ("ab", "ab"), ("ba", "ba"), ("aba", "aba").
k2 = 1: We can choose any string from L (15 ways).
k3 = 3: There is one way to choose three equal strings - ("a", "a", "a").
k4 = 4: There are no four equal strings in L .
Question LINK
My approach
I am making a TRIE of IT and Calculating The and Array F[i] where F[i] represent the number of times i equal String Occur.
My TRIE:
static class Batman{
int value;
Batman[] next = new Batman[26];
public Batman(int value){
this.value = value;
}
}
MY Insert Function
public static void Insert(String S,int[] F , int start){
Batman temp = Root;
for(int i=start;i<S.length();i++){
int index = S.charAt(i)-'a';
if(temp.next[index]==null){
temp.next[index] = new Batman(1);
F[1]+=1;
}else{
temp.next[index].value+=1;
int xx = temp.next[index].value;
F[xx-1]-=1;
F[xx]+=1;
// Calculating The Frequency of I equal Strings
}
temp = temp.next[index];
}
}
MY MAIN FUNCTION
public static void main(String args[] ) throws java.lang.Exception {
Root = new Batman(0);
int n = in.nextInt();
int Q = in.nextInt();
String S = in.next();
int[] F = new int[n+1];
for(int i=0;i<n;i++)
Insert(S,F,i);
long[] ans = new long[n+1];
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
ans[i]+= F[j]*C[j][i]; // C[n][k] is the Binomial Coffecient
ans[i]%=mod;
}
}
while(Q>0){
Q--;
int cc = in.nextInt();
long o =0;
if(cc<=n) o=ans[cc];
System.out.println(o+" "+S.length());
}
}
Why My appraoch is giving TLE as time Complexity is O(N*N) ans the length of String is N<=5000. Please Help me Working CODE
One reason this program get TLE (keep in mind that time constraint is 1 sec):
Each time you create a Batman object, it will create an array with length [26], and it is equivalence to adding a loop with n = 26.
So, you time complexity is 26*5000*5000 = 650000000 = 6.5*10^8 operations, theoretically, it can still fit into time limit if CPU speed is 10^9 operations per sec, but also keep in mind that there are some heavy calculation stuffs after this, so, this should be the reason.
To solve this problem, I used Z-algorithm and get accepted: Link
The actual code is quite complex, so the idea is, you have a table count[i][j], which is the number of substring that matched substring (i, j). Using Z-algorithm, you can have a time complexity of O(n^2).
For each string s:
int n = in.nextInt();
int q = in.nextInt();
String s = in.next();
int[][] cur = new int[n][];
int[][] count = new int[n][n];
int[] length = new int[n];
for (int i = 0; i < n; i++) {
cur[i] = Z(s.substring(i).toCharArray());//Applying Z algorithm
for (int j = 1; j < cur[i].length; j++) {
if (cur[i][j] > length[j + i]) {
for (int k = i + length[j + i]; k < i + cur[i][j]; k++) {
count[i][k]++;
}
length[j + i] = cur[i][j];
}
}
}
int[] F = new int[n + 1];
for(int i = 0; i < n; i++){
for(int j = i; j < n; j++){
int v = count[i][j] + (length[i] < (j - i + 1) ? 1 : 0);
F[v]++;
}
}
Z-algorithm method:
public static int[] Z(char[] s) {
int[] z = new int[s.length];
int n = s.length;
int L = 0, R = 0;
for (int i = 1; i < n; i++) {
if (i > R) {
L = R = i;
while (R < n && s[R - L] == s[R])
R++;
z[i] = R - L;
R--;
} else {
int k = i - L;
if (z[k] < R - i + 1) {
z[i] = z[k];
} else {
L = i;
while (R < n && s[R - L] == s[R])
R++;
z[i] = R - L;
R--;
}
}
}
return z;
}
Actual code: http://ideone.com/5GYWeS
Explanation:
First, we have an array length, with length[i] is the longest substring that matched with the string start from index i
For each index i, after calculate the Z function, we see that, if cur[i][j] > length[j + i], which means, there exists one substring longer than previous substring matched at index j + i, and we havent counted them in our result, so we need to count them.
So, even there are 3 nested for loop, but each substring is only counted once, which make this whole time complexity is O(n ^2)
for (int j = 1; j < cur[i].length; j++) {
if (cur[i][j] > length[j + i]) {
for (int k = i + length[j + i]; k < i + cur[i][j]; k++) {
count[i][k]++;
}
length[j + i] = cur[i][j];
}
}
For below loop, we notice that, if there is a matched for substring (i,j), length[i] >= length of substring (i,j), but if there is no matched, we need to add 1 to count substring (i,j), as this substring is unique.
for(int j = i; j < n; j++){
int v = count[i][j] + (length[i] < (j - i + 1) ? 1 : 0);
F[v]++;
}
Related
I am working on the LeetCode question Longest Substring Without Repeating Characters. But I got two different results between Run Code and Submit Solution. My c++ code is
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int* a = new int[257];
int ans = 0;
int n = s.size();
for (int j = 0, i = 0; j < n; j++) {
i = i > a[s[j]] ? i : a[s[j]];
ans = ans > j - i +1 ? ans : j - i + 1;
a[s[j]] = j + 1;
}
return ans;
}
};
And two outputs are
I don't know what's wrong with my code. Besides, my c++ code is written by learning his website java answer
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
int[] index = new int[128]; // current index of character
// try to extend the range [i, j]
for (int j = 0, i = 0; j < n; j++) {
i = Math.max(index[s.charAt(j)], i);
ans = Math.max(ans, j - i + 1);
index[s.charAt(j)] = j + 1;
}
return ans;
}
}
for (int j = 0, i = 0; j < n; j++) {
i = i > a[s[j]] ? i : a[s[j]];
Since a is uninitialized, a[s[j]] is undefined behavior. You want
for (int i=0;i<257;i++)
a[i]=0;
or better a vector
vector<int> a(257,0);
Unlike Java, C++ does not zero heap memory for you.
int* a = new int[257];
What is the data inside your array a?
In Java, a[0], a[1], ... a[256] are all equal to zero. But in C++, a[0], a[1], ... a[256] contains random garbage from whatever data was previously at that memory address.
You have to zero the memory first:
std::fill_n(a, 257, 0);
Or, if you prefer memset:
std::memset(a, 0, sizeof(int) * 257);
EDIT: As pointed out by #It'scominghome, value-initialization (C++11) is also possible:
int* a = new int[257](); // will zero the array
I have a String (lowercase letters) of size N with 1 based index.
Now I am given Q queries and each query consists of two integers x,y.
For every query I have to print min number of deletion from substring(x,y) (x,y included) such that substring has same frequency of distinct characters.
For example: consider a query which forms the substring abbccd,
now min no. of deletions in it are 2(1b,1c).
1<= N,Q <= 10^5
1<=X<=Y<=N
I tried the brute force approach where for every query I calculated frequency of characters and then calculated deletion by equating all frequencies to min frequency.
But I know that this approach is wrong and will also result in TLE. Can anyone help me with it.
My code:
import java.util.Arrays;
import java.util.Scanner;
public class q1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int q = in.nextInt();
in.nextLine();
String s = in.nextLine();
int x,y;
while(q>0){
x = in.nextInt();
y = in.nextInt();
String temp = s.substring(x-1, y);
char c[] = temp.toCharArray();
int count[] = new int[26];
//calculating the frequencies.
for (int i = 0; i < c.length; i++) {
count[c[i]-'a']++;
}
int min = Integer.MAX_VALUE;
for (int i = 0; i < count.length; i++) {
if(count[i]!=0 && count[i]<min){
min = count[i];
}
}
int deletions = 0;
for (int i = 0; i < count.length; i++) {
if(count[i]!=0){
deletions += (count[i]-min);
}
}
System.out.println(deletions);
q--;
}
}
}
Problem Link
Here is my solution with O(26*26) steps per query.
Made a cumulative frequency array to store the frequency of every character up-til the "ith" position in the string.
dp[i][0] is the frequency of "a" in the string from the index 1 to i inclusive.
dp[i][1] is the frequency of "b" in the string from the index 1 to i inclusive.
and so on up-til 25 for "z".
Now in every query we can calculate the frequency of every character in the sub-string x to y, by subtracting frequency of a character "i" up-til "x-1" from the frequency of the same character up-til "y" i.e.
freq[i] = dp[x-1][i] - dp[y][i];
Now once we have the frequency of every character in the sub-string we can iterate over these frequencies and calculate the number of deletions for every unique character.
For example if we have a sub-string "ddddbbacc"
freq of "a" is 1
freq of "b" is 2
freq of "c" is 2
freq of "d" is 4
frequencies = [1, 2, 2, 4]
at first iteration we try to make all the frequencies equal to freq[0] i.e 1.
to do so we delete one char from the characters having freq = 2 and 3 characters from the character having freq = 4.
this gives us a answer of 1+1+3 = 5.
In second iteration we try to make all the frequencies equal to freq[1] i.e 2.
to do so we delete 3 characters from the character having freq = 4.
and we also delete all the characters whose freq < 2
this gives us a answer of 1+2 = 3.
In worst case it takes 26*26 steps per query.
Found the question
here
import java.util.Arrays;
import java.util.Scanner;
import java.util.Collections;
import java.util.ArrayList;
class q1 {
static int[][] dp = new int[100005][27];
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int q = in.nextInt();
in.nextLine();
String s = in.nextLine();
for (int i = 0; i < s.length(); i++){ //O(N*26)
for (int j = 0; j < 26; j++)
dp[i+1][j] = dp[i][j];
dp[i+1][(int)(s.charAt(i))-(int)('a')]++;
}
int x,y;
while(q>0){ //O(Q*700)
x = in.nextInt();
y = in.nextInt();
int freq[] = new int[27];
for(int i = 0; i < 26; i++) //O(1)
freq[i] = dp[y][i] - dp[x-1][i];
ArrayList<Integer> f = new ArrayList<Integer>();
for(int i = 0; i < 26; i++) //O(1)
if(freq[i] != 0)
f.add(freq[i]);
Collections.sort(f); //O(26*log26)
int ans = s.length() + 1;
int drop = 0;
for(int i = 0; i < f.size(); i++){ //O(26*26)
int currans = drop;
for(int j = i+1; j < f.size(); j++)
currans += (f.get(j)-f.get(i));
if (ans > currans)
ans = currans;
drop += f.get(i);
}
if (drop < ans)
ans = drop;
System.out.println(ans);
q--;
}
}
}
This is my code, but I know this is not right. I have written a lot of code for such a simple task.
Sample input is:
welcome
Sample output is:
com
elc
lco
ome
wel
It should print:
your first string is 'com'
and
your last string is 'wel'
Code:
import java.io.*;
import java.util.*;
public class Solution {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
int k = sc.nextInt();
int k1 = k;
int j = 0;
int t = str.length();
String [] s = new String [1000];
for (int i = t, a = 0; i >= k; i--, a++) {
s[a] = str.substring(j, k1);
j++;
k1++;
}
String[] s1 = new String[j];
for (int i = 0 ; i < j; i++) {
s1[i] = s[i];
}
for (int y = 0; y < j; y++) {
for (int z = y + 1; z < j; z++) {
if(s1[z].compareTo(s1[y]) < 0) {
String temp = s1[z];
s1[z] = s1[y];
s1[y] = temp;
}
}
}
System.out.println(s1[0]);
System.out.println(s1[1]);
}
}
Note: I split my strings, but I'm not able to arrange strings in alphabetical order, and feel that I have used a lot of arrays. Is there a better way to do this?
You can
reduce the number of variables,
use collections (list in this case) instead of Arrays to avoid having to set a size (1000)
Sort using the framework
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
int k = sc.nextInt();
List<String> cutStrings = new ArrayList<String>();
for (int i = 0; i < str.length() - k; i++) {
cutStrings.add(str.substring(i, i + k));
}
Collections.sort(cutStrings);
System.out.println(cutStrings.get(0));
System.out.println(cutStrings.get(cutStrings.size()-1));
}
You can easily sort your String[] array by simply using
Arrays.sort(s);
This will sort your strings in the default order. If you need any other kind of order you can pass the comparator as a second parameter.
You can get first and last by getting s[0] and s[s.length-1]
I did a quick implementation of your requirements. It might not be exactly what you're looking for but it should get you started. :)
So, I used an ArrayList to grab the substrings and the use the Collections library to do the sorting for me. This is just one of the many ways of solving the problem, btw. The input word can vary in size so I felt that a list would be appropriate for this situation.
String s = "welcome";
List<String> words = new ArrayList<String>();
for (int i = 0; i < s.length() - 2; i++) {
String chunk = s.charAt(i) + "" + s.charAt(i + 1) + ""
+ s.charAt(i + 2);
words.add(chunk);
System.out.println(chunk);
}
Collections.sort(words);
System.out.println(words.toString());
Feel free to let me know if you have any questions or if I have made a mistake in the code.
Good luck!
Actual problem of your code is splitting. Sorting will work. If j value 1 and k1 value 3 then wel substring is coming. Next loop, (after incrementation of both j and k1 by 1) j value 2 and k1 value 4 then elc substring is coming, etc.
So, instead of
String [] s = new String [1000];
for (int i = t, a = 0; i >= k; i--, a++) {
s[a] = str.substring(j, k1);
j++;
k1++;
}
use
int k = sc.nextInt();
String [] s = new String [(str.length()/3)+1] ;
for ( int i = 0,a = 0; i<(str.length()-k); i+=k,a++)
{
s[a] = str.substring(i,(i+k));
System.out.println(s[a]);
}
s[s.length-1]=str.substring((str.length()-k),str.length());//to add remaining values
Arrays.sort(s);//sorting alphabatically
for(int i = 0; i < s.length; i++)
System.out.println(s[i]);
}
i value will be incremented by 3. In the for loop (i+=k) where k=3.
Output:
amp
com
e s
ple
wel
I need some help inserting the number 8 into an array that gives me random values. The array must be in order. For example if I had an array of (1,5,10,15), I have to insert the number 8 between 5 and 10. I am having a problem on how I can figure our a way to find the index where 8 will be placed because the array is random, it can be anything. Here is my code so far :
public class TrickyInsert {
public static void main(String[] args) {
int[] mysteryArr = generateRandArr();
//print out starting state of mysteryArr:
System.out.print("start:\t");
for ( int a : mysteryArr ) {
System.out.print( a + ", ");
}
System.out.println();
//code starts below
// insert value '8' in the appropriate place in mysteryArr[]
int[] tmp = new int[mysteryArr.length + 1];
int b = mysteryArr.length;
for(int i = 0; i < mysteryArr.length; i++) {
tmp[i] = mysteryArr[i];
}
tmp[b] = 8;
for(int i =b ; i<mysteryArr.length; i++) {
tmp[i+1] = mysteryArr[i];
}
mysteryArr = tmp;
any tips? thanks!
Simply add the number then use Arrays.sort method,
int b = mysteryArr.length;
int[] tmp = new int[b + 1];
for(int i = 0; i < b; i++) {
tmp[i] = mysteryArr[i];
}
tmp[b] = 8;
mysteryArr = Arrays.sort(tmp);
In your example the random array is sorted. If this is the case, just insert 8 and sort again.
Simply copy the array over, add 8, and sort again.
int[] a = generateRandArr();
int[] b = Arrays.copyOf(a, a.length + 1);
b[a.length] = 8;
Arrays.sort(b);
int findPosition(int a, int[] inputArr)
{
for(int i = 0; i < inputArr.length; ++i)
if(inputArr[i] < a)
return i;
return -1;
}
int[] tmpArr = new int[mysteryArr.length + 1];
int a = 8; // or any other number
int x = findPosition(a, mysteryArr);
if(x == -1)
int i = 0;
for(; i < mysteryArr.length; ++i)
tmpArr[i] = mysteryArr[i];
tmpArr[i] = a;
else
for(int i = 0; i < mysteryArr.length + 1; ++i)
if(i < x)
tmpArr[i] = mysteryArr[i];
else if(i == x)
tmpArr = a;
else
tmpArr[i] = mysteryArr[i - 1];
I will suggest using binary search to find the appropriate index. Once you locate the index, you can use
System.arraycopy(Object src, int srcIndex, Obj dest, int destIndex, int length)
to copy the left half to your new array (with length one more than the existing one) and then the new element and finally the right half. This will stop the need to sort the whole array every time you insert an element.
Also, the following portion does not do anything.
for(int i =b ; i<mysteryArr.length; i++) {
tmp[i+1] = mysteryArr[i];
}
since int b = mysteryArr.length;, after setting int i =b ;, i<mysteryArr.length; will be false and hence the line inside this for loop will never execute.
I'm trying to resolve all the combinations of elements based on a given string.
The string is like this :
String result="1,2,3,###4,5,###6,###7,8,";
The number of element between ### (separated with ,) is not determined and the number of "list" (part separated with ###) is not determined either.
NB : I use number in this example but it can be String too.
And the expected result in this case is a string containing :
String result = "1467, 1468, 1567, 1568, 2467, 2468, 2567, 2568, 3467, 3468, 3567, 3568"
So as you can see the elements in result must start with an element of the first list then the second element must be an element of the second list etc...
From now I made this algorithm that works but it's slow :
String [] parts = result.split("###");
if(parts.length>1){
result="";
String stack="";
int i;
String [] elmts2=null;
String [] elmts = parts[0].split(",");
for(String elmt : elmts){ //Browse root elements
if(elmt.trim().isEmpty())continue;
/**
* This array is used to store the next index to use for each row.
*/
int [] elmtIdxInPart= new int[parts.length];
//Loop until the root element index change.
while(elmtIdxInPart[0]==0){
stack=elmt;
//Add to the stack an element of each row, chosen by index (elmtIdxInPart)
for(i=1 ; i<parts.length;i++){
if(parts[i].trim().isEmpty() || parts[i].trim().equals(","))continue;
String part = parts[i];
elmts2 = part.split(",");
stack+=elmts2[elmtIdxInPart[i]];
}
//rollback i to previous used index
i--;
if(elmts2 == null){
elmtIdxInPart[0]=elmtIdxInPart[0]+1;
}
//Check if all elements in the row have been used.
else if(elmtIdxInPart[i]+1 >=elmts2.length || elmts2[elmtIdxInPart[i]+1].isEmpty()){
//Make evolve previous row that still have unused index
int j=1;
while(elmtIdxInPart[i-j]+1 >=parts[i-j].split(",").length ||
parts[i-j].split(",")[elmtIdxInPart[i-j]+1].isEmpty()){
if(j+1>i)break;
j++;
}
int next = elmtIdxInPart[i-j]+1;
//Init the next row to 0.
for(int k = (i-j)+1 ; k <elmtIdxInPart.length ; k++){
elmtIdxInPart[k]=0;
}
elmtIdxInPart[i-j]=next;
}
else{
//Make evolve index in current row, init the next row to 0.
int next = elmtIdxInPart[i]+1;
for(int k = (i+1) ; k <elmtIdxInPart.length ; k++){
elmtIdxInPart[k]=0;
}
elmtIdxInPart[i]=next;
}
//Store full stack
result+=stack+",";
}
}
}
else{
result=parts[0];
}
I'm looking for a more performant algorithm if it's possible. I made it from scratch without thinking about any mathematical algorithm. So I think I made a tricky/slow algo and it can be improved.
Thanks for your suggestions and thanks for trying to understand what I've done :)
EDIT
Using Svinja proposition it divide execution time by 2:
StringBuilder res = new StringBuilder();
String input = "1,2,3,###4,5,###6,###7,8,";
String[] lists = input.split("###");
int N = lists.length;
int[] length = new int[N];
int[] indices = new int[N];
String[][] element = new String[N][];
for (int i = 0; i < N; i++){
element[i] = lists[i].split(",");
length[i] = element[i].length;
}
// solve
while (true)
{
// output current element
for (int i = 0; i < N; i++){
res.append(element[i][indices[i]]);
}
res.append(",");
// calculate next element
int ind = N - 1;
for (; ind >= 0; ind--)
if (indices[ind] < length[ind] - 1) break;
if (ind == -1) break;
indices[ind]++;
for (ind++; ind < N; ind++) indices[ind] = 0;
}
System.out.println(res);
This is my solution. It's in C# but you should be able to understand it (the important part is the "calculate next element" section):
static void Main(string[] args)
{
// parse the input, this can probably be done more efficiently
string input = "1,2,3,###4,5,###6,###7,8,";
string[] lists = input.Replace("###", "#").Split('#');
int N = lists.Length;
int[] length = new int[N];
int[] indices = new int[N];
for (int i = 0; i < N; i++)
length[i] = lists[i].Split(',').Length - 1;
string[][] element = new string[N][];
for (int i = 0; i < N; i++)
{
string[] list = lists[i].Split(',');
element[i] = new string[length[i]];
for (int j = 0; j < length[i]; j++)
element[i][j] = list[j];
}
// solve
while (true)
{
// output current element
for (int i = 0; i < N; i++) Console.Write(element[i][indices[i]]);
Console.WriteLine(" ");
// calculate next element
int ind = N - 1;
for (; ind >= 0; ind--)
if (indices[ind] < length[ind] - 1) break;
if (ind == -1) break;
indices[ind]++;
for (ind++; ind < N; ind++) indices[ind] = 0;
}
}
Seems kind of similar to your solution. Does this really have bad performance? Seems to me that this is clearly optimal, as the complexity is linear with the size of the output, which is always optimal.
edit: by "similar" I mean that you also seem to do the counting with indexes thing. Your code is too complicated for me to go into after work. :D
My index adjustment works very simply: starting from the right, find the first index we can increase without overflowing, increase it by one, and set all the indexes to its right (if any) to 0. It's basically counting in a number system where each digit is in a different base. Once we can't even increase the first index any more (which means we can't increase any, as we started checking from the right), we're done.
Here is a somewhat different approach:
static void Main(string[] args)
{
string input = "1,2,3,###4,5,###6,###7,8,";
string[] lists = input.Replace("###", "#").Split('#');
int N = lists.Length;
int[] length = new int[N];
string[][] element = new string[N][];
int outCount = 1;
// get each string for each position
for (int i = 0; i < N; i++)
{
string list = lists[i];
// fix the extra comma at the end
if (list.Substring(list.Length - 1, 1) == ",")
list = list.Substring(0, list.Length - 1);
string[] strings = list.Split(',');
element[i] = strings;
length[i] = strings.Length;
outCount *= length[i];
}
// prepare the output array
string[] outstr = new string[outCount];
// produce all of the individual output strings
string[] position = new string[N];
for (int j = 0; j < outCount; j++)
{
// working value of j:
int k = j;
for (int i = 0; i < N; i++)
{
int c = length[i];
int q = k / c;
int r = k - (q * c);
k = q;
position[i] = element[i][r];
}
// combine the chars
outstr[j] = string.Join("", position);
}
// join all of the strings together
//(note: joining them all at once is much faster than doing it
//incrementally, if a mass concatenate facility is available
string result = string.Join(", ", outstr);
Console.Write(result);
}
I am not a Java programmer either, so I adapted Svinja's c# answer to my algorithm, assuming that you can convert it to Java also. (thanks to Svinja..)