Similar Char(TCS CodeVita) Reduce Time Complexity - c++

You have to take the length of the string from the user then input the string from the user, after that take the no. of queries from the user to check. Suppose that the user gives 3 queries 4,5,7. Then 4,5,7 are the position where you have to check how many of the same characters are repeated before that position.
Inputs:
9 (Length of the string to input)
abcabcabc
3 (No. of queries to check)
4 (Check at position 4)
5 (Check at position 5)
7 (Check at position 7)
Output:
1
1
2
Code which I made:
#include <iostream>
int main()
{
int n; // Length of String
int a[10000]; // Input for Query
int q; // NO. of queries
std::cin >> n;
char ch[n]; // To store the InputString
for (int i = 1; i <= n; i++) {
std::cin >> ch[i];
}
std::cin >> q;
for (int j = 1; j <= q; j++) {
std::cin >> a[j];
}
for (int i = 1; i <= q; i++) {
int count = 0;
for (int j = 1; j < a[i]; j++) {
if (ch[j] == ch[a[i]]) {
count = count + 1;
}
}
std::cout << count << "\n";
}
return 0;
}
But the problem is this the time complexity of the program is too much.In worst case it would be O(n*q), where n = length of the string and q = number of queries. How to improve the time complexity?

First, your Output does not meet the assignment. For example, at the position [1], there is character 'b' (0-based indexing), which has zero repetitions before that position. At the position [2], there is character 'a', which has one repetition before that position. So do you mean, before, including?
Second, is that a homework? (Just curious. Can hardly imagine any real-life case here... :))
Finally, to your question:
1) make an array of <query position, count> pairs
2) go through the InputString only once up to the highest query position and at every position in the InputString, increase your count if relevant for the query position in the <query position, count> pair.
2.b) Slight further optimization: sort your array or <query position, count> pairs according to the query position first. In your InputString, go to the lowest query position so that you have to check for every <query position, count> pair in this first turn. Then continue searching your InputString from the [lowest query position + 1] position up to the second lowest query position. You do not need to care about the lowest <query position, count> pair in this second turn. Etc. ...

Related

How to match a list of bounds with a list of numbers, one-to-one

The task
I am solving a leetcode style task. Find the amout of values, that can be sortet into a bound (lower <= value <= upper).
Input: first line contains n & m, the amout of bounds and the amount of values.
The second line contains the n lower bounds.
The third line contains the n upper bounds.
The fourth line contains m the values.
(A single bound is li and ri)
Now find the amout of Values, that can be sorted into the bounds, if we do it optimally. (and output it)
Note: Each value can only be assigned one bound and one bound only to one value.
Ex. Input:
3 4
1 7 3
4 9 8
5 2 9 2
outputs 3.
My solution
I have a struct for the bounds:
struct Bounds {
int min, max;
// operator for sorting both primary and secondary keys
bool operator < (const Bounds& rhs) const
{
if ( min == rhs.min )
return max > rhs.max;
else
return min > rhs.min;
}
};
and solve as follows:
int main()
{
int n, m, rval = 0;
cin >> n >> m;
Bounds newBound = {0, 0};
vector<Bounds> bounds(n, newBound);
vector<int> values(m);
// get input
for (int i = 0; i < n; i++)
cin >> bounds[i].min;
for (int i = 0; i < n; i++)
cin >> bounds[i].max;
for (int i = 0; i < m; i++)
cin >> values[i];
// sort Bounds in descending order by lower bound as primary and upper bound as secondary
std::sort(bounds.begin(), bounds.end(), less<Bounds>());
//sort values in ascending order
std::sort(values.begin(), values.end(), greater<int>());
// for every bound
for (int j = 0; j < m; j++) {
int value = values[j];
for (int i = 0; i < bounds.size(); i++)
{
if (bounds[i].min <= value && value <= bounds[i].max) {
rval++;
bounds.erase(bounds.begin() + i);
break;
}
}
}
cout << rval << "\n";
}
My Problem
This works, but:
I don't think this is the optimal solution,
It's to slow, as one Testcase with 10^6 values takes ~7 minutes to solve.
How can I improve it? Or should I scrap this approach?
I think my solution has 0(n^2 + (n log(n)))
Or Phrased differently: How can I remove the nested for loop?

CodeForces stones on table, code cannot pass one test case I cannot understand why

This is the question - There are n stones on the table in a row, each of them can be red, green or blue. Count the minimum number of stones to take from the table so that any two neighboring stones had different colors. Stones in a row are considered neighboring if there are no other stones between them.
Input - The first line contains integer n (1 ≤ n ≤ 50) — the number of stones on the table.
The next line contains string s, which represents the colors of the stones. We'll consider the stones in the row numbered from 1 to n from left to right. Then the i-th character s equals "R", if the i-th stone is red, "G", if it's green and "B", if it's blue.
Output - The answer
My code - ```
int main(){
string s;
int q,a,b,c;
cin >> q;
cin >> s;
a = s.length();
for(int i=0;i<q;i++){
if((s[i]=='R'&&s[i+1]=='R')||(s[i]=='G'&&s[i+1]=='G')||(s[i]=='B'&&s[i+1]=='B')){
s.erase(i+1,i+1);
}
}
b = s.length();
c = a - b;
if((s[0]=='R'&&s[1]=='R')||(s[0]=='G'&&s[1]=='G')||(s[0]=='B'&&s[1]=='B')){
cout << c+1;
}else{ cout << c;
}
}
When the input is "4 RBBR", the output shows 2 instead of 1, i'm not understanding why its doing that, could someone help me.
Here are some other test cases which my code passes -
"3
RRG"
"5
RRRRR"
int main()
{
string s;
size_t q, a, c;
cin >> q;
cin >> s;
a = s.length();
// You may not use q here, because you are modifying the string length, so when the
// loop enters the next iteration, q is no longer valid.
// Since you are comparing it with the next item, you must reduce the count by 1, otherwise you read out of bounds.
// It's also wrong to use q here, because the user can enter an invalid length. i.E. "5 RR".
for (size_t i = 0; i < s.length()-1; i++)
{
if (s[i] == s[i + 1])
{
// Here 'i' may not be increased, because the next character can also be a neighbor now
s.erase(i + 1, 1);
i--;
}
}
c = a - s.length();
cout << c;
return 0;
}

Given two string S and T. Determine a substring of S that has minimum difference with T?

I have two string S and T where length of S >= length of T. I have to determine a substring of S which has same length as T and has minimum difference with T. Here difference between two strings of same length means, the number of indexes where they differ. For example: "ABCD" and "ABCE" differ at 3rd index, so their difference is 1.
I know I can use KMP(Knuth Morris Pratt) Pattern Searching algorithm to search T within S. But, what if S doesn't contain T as a substring? So, I have coded a brute force approach to solve this:
int main() {
string S, T;
cin >> S >> T;
int SZ_S = S.size(), SZ_T = T.size(), MinDifference = INT_MAX;
string ans;
for (int i = 0; i + SZ_T <= SZ_S; i++) { // I generate all the substring of S
int CurrentDifference = 0; // and check their difference with T
for (int j = 0; j < SZ_T; j++) { // and store the substring with minimum difference
if (S[i + j] != T[j])
CurrentDifference++;
}
if (CurrentDifference < MinDifference) {
ans = S.substr (i, SZ_T);
MinDifference = CurrentDifference;
}
}
cout << ans << endl;
}
But, my approach only works when S and T has shorter length. But, the problem is S and T can have length as large as 2 * 10^5. How can I approach this?
Let's maximize the number of characters that match. We can solve the problem for each character of the alphabet separately, and then sum up the results for
substrings. To solve the problem for a particular character, give string S and T as sequences 0 and 1 and multiply them using the FFT https://en.wikipedia.org/wiki/Fast_Fourier_transform.
Complexity O(|A| * N log N) where |A| size of the alphabet (for an uppercase letter is 26).

Finding all possible combinations with 1 element in each row of a 2D array

Recently I have been trying to do a problem that requires me to find all the different combinations with selecting only 1 element from each row. For example, I'm inputting n rows with 2 strings per row. However, I only want to find all the different combinations where I choose 1 string from each row.
Example:
Input:
3
alex bob
straw mat
eat drink
Example combination:
alex straw drink
This results in 2^n combinations, which in this case would be 2^3 = 8 combinations. However, if I was to use n for loops to find the combinations
e.g.
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
int n;
int main(int argc, char ** argv) {
cin >> n; //rows of words
string words[n][2]; //the words with 2 words per row
for (int i = 0; i < n; i++) {
for (int j = 0; j < 2; j++) {
cin >> words[i][j]; //input of words
}
}
//finding all possible combinations
for (int i =0; i<n; i++){
for (int j=0; j<2; j++){
for (int x=0; x<2; x++){
//and so on per n
}
}
}
return 0;
}
this would take n for loops to find out all the combinations of the array with only taking one item from each row. What would be the best and simplest approach to finding all different combinations with size n as I would take 1 string out of the two in each row? Thanks.
You can do recursion.
Assuming C++11, something like this maybe (didn't try to compile this though):
// finding all possible combinations
std::vector<std::vector<std::string>> combinations;
const auto processLine = [&](const std::vector<std::string>& currentCombination, int line) {
std::vector<std::string> combination0 = currentCombination;
std::vector<std::string> combination1 = currentCombination;
combination0.push_back(words[line][0]);
combination1.push_back(words[line][1]);
if (line + 1 < n) {
// process next line
processLine(combination0, line + 1);
processLine(combination1, line + 1);
}
else {
// last line - keep the result
combinations.push_back(combination0);
combinations.push_back(combination1);
}
};
std::vector<std::string> empty;
processLine(empty, 0);
// print results
for (const auto& combination : combinations) {
for (const auto& word : combination) {
std::cout << word << " ";
}
std::cout << std::endl;
}
A very simple solution for a setting where you have always 2 elements per row would be to use datatype integer and interpret each bit as a decision for the first or the second column in the respective row; then simply count from 0 to 2^n - 1 in order to get all combinations.
Applied to your example this would look as follows:
int bits meaning
0 000 alex,straw,eat
1 001 alex,straw,drink
2 010 alex,mat,eat
3 011 alex,mat,dring
4 100 bob,straw,eat
5 101 bob,straw,drink
6 110 bob,mat,eat
7 111 bob,mat,drink
For any of the given integer values 0..7, use bit shift operators or &-bitmask to map each bit to a column index:
void getCombinationRepresentedByIntValue(vector<string>& combination, int value) {
int mask = 1;
for (int i=n-1; i>=0; i--) {
if (value & mask)
combination.push_back(words[i][1]);
else
combination.push_back(words[i][0]);
mask = mask << 1;
}
}
That seems to answer your question :
int ct[n]; // count of the number of pass
int current = 0; // index of the current word (n)
/* while not all combinaison have been exploited */
while (current >= 0)
{
cout << words[current][ct[current]]; /* <<<<< can be used another way*/
/* down to the next word */
current ++; // to get the next word
if (current >=n) { // at the end of the list
cout << " ";
current--; // restore last
ct[current]++; // increment number of time we passed
/* find the previous not completely exploited */
while (current >= 0 && ct[current]> 1) /* <<< change 1 to any number of words per line */
{
ct[current] = 0;
current--;
if (current >= 0) ct[current]++;
}
if (current > 0 ) current = 0;
}
}
With your example :
Input :
3
alex bob
straw mat
eat drink
output :
alexstraweat
alexstrawdrink
alexmateat
alexmatdrink
bobstraweat
bobstrawdrink
bobmateat
bobmatdrink
hope it helps !

Generate (not so)random string with particular string occurences

I have a requirement where I have the alphabet 'ACGT' and I need to create a string of around 20,000 characters. This string should contain 100+ occurrences of the pattern "CCGT". Most of the time the generated string contains it around 20-30 instances.
int N = 20000;
std::string alphabet("ACGT");
std::string str;
str.reserve(N);
for (int index = 0; index < N; index++)
{
str += alphabet[rand() % (alphabet.length())];
}
How do I tweak the code so that the pattern would appear more often?
Edit - Is there a way of changing the alphabet, i.e - 'A', 'C', 'G', 'T', 'CCGT' as characters of the alphabet?
Thank you.
Generate an array of ints containing 100 x 0s and 490 1s, 2s, 3s and 4s
[000000....111111....2222 etc] making almost 20,000 entries.
Then random shuffle it (std::random_shuffle)
Then write a string where each 0 translates to 'CCGT', each 1 translates to 'A', each 2 .... etc
I think that gives you what you want, and by tweaking the original array of ints you could change the number of 'A' characters in the output too.
Edit: If that isn't random enough, do 100 0s at the start and then random 1-4 for the rest.
The only solution I can think of that would meet the "100+" criteria is:
create 20000 character string
number of instances (call it n) = 100 + some random value
for (i = 0 ; i < n ; ++i)
{
pick random start position
write CCGT
}
Of course, you'd need to ensure the overwritten characters weren't part of a "CCGT" already.
My first thought would be to generate a list of 100 indexes where you will definitely insert the special string. Then as you generate the random string, insert the special string at each of these indexes as you reach them.
I've missed out checking that the intervals are spaced appropriately (cannot be within 4 of another interval) and also sorting them in ascending order - both of which would be necessary for this to work.
int N = 20000;
std::string alphabet("ACGT");
int intervals[100];
for (int index = 0; index < 100; index)
{
intervals[index] = rand() % 2000;
// Do some sort of check to make sure each element of intervals is not
// within 4 of another element and that no elements are repeated
}
// Sort the intervals array in ascending order
int current_interval_index = 0;
std::string str;
str.reserve(N);
for (int index = 0; index < N; index++)
{
if (index == intervals[current_interval_index])
{
str += alphabet;
current_interval_index++;
index += 3;
}
else
{
str += alphabet[rand() % (alphabet.length())];
}
}
The solution I came up with uses a std::vector to contain all the random sets of 4 chars including the 100 special sequence. I then shuffle that vector to distribute the 100 special sequence randomly throughout the string.
To make the distribution of letters even I create an alternative alphabet string called weighted that contains a relative abundance of alphabet characters according to what has already been included from the 100 special sequence.
int main()
{
std::srand(std::time(0));
using std::size_t;
const size_t N = 20000;
std::string alphabet("ACGT");
// stuff the ballot
std::vector<std::string> v(100, "CCGT");
// build a properly weighted alphabet string
// to give each letter equal chance of appearing
// in the final string
std::string weighted;
// This could be scaled down to make the weighted string much smaller
for(size_t i = 0; i < (N - 200) / 4; ++i) // already have 200 Cs
weighted += "C";
for(size_t i = 0; i < (N - 100) / 4; ++i) // already have 100 Ns & Gs
weighted += "GT";
for(size_t i = 0; i < N / 4; ++i)
weighted += "A";
size_t remaining = N - (v.size() * 4);
// add the remaining characters to the weighted string
std::string s;
for(size_t i = 0; i < remaining; ++i)
s += weighted[std::rand() % weighted.size()];
// add the random "4 char" sequences to the vector so
// we can randomly distribute the pre-loaded special "4 char"
// sequence among them.
for(std::size_t i = 0; i < s.size(); i += 4)
v.push_back(s.substr(i, 4));
// distribute the "4 char" sequences randomly
std::random_shuffle(v.begin(), v.end());
// rebuild string s from vector
s.clear();
for(auto&& seq: v)
s += seq;
std::cout << s.size() << '\n'; // should be N
}
I like the answer by #Andy Newman and think that is probably the best way - the code below is a compilable example of what they suggested.
#include <string>
#include <algorithm>
#include <iostream>
int main()
{
srand(time(0));
int N = 20000;
std::string alphabet("ACGT");
std::string str;
str.reserve(N);
int int_array[19700];
// Populate int array
for (int index = 0; index < 19700; index++)
{
if (index < 100)
{
int_array[index] = 0;
}
else
{
int_array[index] = (rand() % 4) + 1;
}
}
// Want the array in a random order
std::random_shuffle(&int_array[0], &int_array[19700]);
// Now populate string from the int array
for (int index = 0; index < 19700; index++)
{
switch(int_array[index])
{
case 0:
str += alphabet;
break;
case 1:
str += 'A';
break;
case 2:
str += 'C';
break;
case 3:
str += 'G';
break;
case 4:
str += 'T';
break;
default:
break;
}
}
// Print out to check what it looks like
std::cout << str << std::endl;
}
You should make N larger.
I take this liberty because you say 'create a string of around 20,000 characters'; but there's more to it than that.
If you're only finding around 20-30 instances in a string of 20000 characters then something is wrong. A ballpark estimate is to say that there are 20000 character positions to test, and at each of these there will be a four-letter string from an alphabet of four letters, giving a 1/256 chance of it being a specific string. The average should be (approximately; because I've oversimplified) 20000/256, or 78 hits.
It could be that your string isn't properly randomised (likely due to the use of the modulo idiom), or perhaps you're testing only every fourth character position -- as if the string were a list of non-overlapping four-letter words.
If you can bring your average hit rate back up to 78, then you can reach a little further to your 100 requirement simply by increasing N proportionally.