 # STRQ - editorial

Author: Sunny Aggarwal
Tester: Pushkar Mishra
Editorialist: Florin Chirica

### PROBLEM

You’re given a string containing only letters c h e and f. You have to answer queries asking how many words start in a given letter and end in other letter (necessarily different by first one) considering only positions from the string between two given numbers.

### QUICK EXPLANATION

Let’s calculate ways[A][B][i] = in how many ways can we choose a word with beginning letter A, ending letter B and its ending position up to i. Also, let cnt[A][i] = how many letters A appear in the first i characters. This information is enough to answer queries in O(1): for two characters A and B and two positions i and j, the answer is ways[A][B][j] - ways[A][B][i - 1] - cntA * cntB, where cntA = number of characters equal to A from [1…i - 1] and cntB = number of characters equal to B from [i…j].

### EXPLANATION

Number of letters is small

A first thing we should observe is the number of letters available is 4 (c h e and f). This low number of letters will help us to solve the problem with a simple and efficient solution.

The idea with having 4 letters is that we can precalculate something, then we can answer all queries in constant time.

Let’s inspect how queries can look like. We start by looking how beginning and ending of a good substring can look like. There are only 12 possibilities.

• (start) c (end) h
• (start) c (end) e
• (start) c (end) f
• (start) h (end) c
• (start) h (end) e
• (start) h (end) f
• (start) e (end) c
• (start) e (end) h
• (start) e (end) f
• (start) f (end) c
• (start) f (end) h
• (start) f (end) e

Since there are only 12 possibilities, we can iterate over them and store something for each configuration. For a configuration, what we computed should be enough to solve all queries that correspond to that configuration.

For a fixed start and end letter

By now we can assume that our start and end letters are fixed (using the iteration). Let’s denote by A the start letter and by B the end letter.

What we need to do is to answer queries: for a subarray [i…j], how many indices i’, j’ exist, such as array[i’] = A and array[j’] = B, with i <= i’ <= j’ <= j.

The trick here is to solve an easier problem firstly: suppose we only had to count pairs (i’, j’) such as i’ <= j’ <= j (we erased i condition). Now the task can be solved by a simple precalculation.

Let ways[A][B][i] = number of ways to choose i’ <= j’ <= i such as array[i’] = A and array[j’] = B. We have two choices for j’.

Choice #1: Let j’ < i. All pairs (i’, j’) such as j’ < i are already calculated in ways[A][B][i - 1], so we can simply add this number and treat all case.
Choice #2: Let j’ = i. Obviously, for this to happen we’re forced to have array[i] = B. What’s left is to find positions i’ such as array[i’] = A and i’ < i. This is easily done by keeping a partial sum for each letter, something like sum[let][i] = how many times does letter let appear in positions [1…i].

Hence, the answer is in ways[A][B][j].

Now let’s consider full restrictions. In reality we have i <= i’ <= j’ <= j. Let’s see what elements are counted above, but should not be counted.

There are two cases

Case #1: i’ < j’ < i <= j

Case #2: i’ < i < j’ <= j

We have to erase from our result the results for those cases.

Case #1 is simple to calculate, as its answer is simply ways[A][B][i - 1].

Case #2 basically asks us to find a letter A in range [1…i - 1] and a letter B in range [i…j]. Let cntA the number of letters A in range [1…i-1] and cntB the number of letters B in range [i…j]. The answer is simply cntA * cntB. The values of cntA and cntB can be calculated by partial sums.

Time Complexity

The computation of ways takes O(n) time (with constant 12). Also, partial sums also take O(n) time. Then, a query is answered in O(1) as stated above.

### AUTHOR’S AND TESTER’S SOLUTIONS:

Tester’s solution

Setter’s solution

3 Likes

http://www.codechef.com/viewsolution/6279777

What would the approach to solve the given problem had the number of characters be 26?

I tried to use segment tree approach but got TLE http://www.codechef.com/viewsolution/6204146
why my this solution was giving WA in contest ?? i checked it for nearly 400 test cases Some one please tell me what’s wrong with my approach.

I took a 2d array and stored the location ( indices ) of each character in the string. Then i took the input of queries and made a binary search and found out the least index at which the character is present in the given range. Then i counted the no. of possibilities. First subtask is accepted but the second and third are resulting in a TLE. Someone please help me in my approach.

http://www.codechef.com/viewsolution/6261829

Simply because it’s too much to do for every query. Every query needs to be answered in O(1).

Hello frendz ,

Hope you like this problem.

May be some of you are annoyed due to the strict time limit for this problem but it was intentionally set to forbid the solutions based on data structures or any other slower solutions.

4 Likes

http://www.codechef.com/viewsolution/6306768

Hey, I got AC in the first 2 subtasks and tle in the 3rd.

(here’s the link : http://www.codechef.com/viewsolution/6307251 )

Then I switched to scanf instead of cin and ended up getting SIGSEGV for all 3 sub tasks.

(here’s the link for the modified code : http://www.codechef.com/viewsolution/6307266 )

Been at it since long now. Any help would be appreciated.

Thanks

It is very helpful for beginners like us.

whats wrong with my code for this question

http://www.codechef.com/viewsolution/6314036

LABOUR! Totally waste of time…!! Not expected from an author like you

please check my sol it is giving correct answer for given test cases but on server it is giving WA…
#include<bits/stdc++.h>
#define ll long long int
using namespace std;
string P;
ll q;
char a,b;
ll *c,*h,*e,*f,*ch,*ce,*cf,*hc,*he,*hf,*ec,*eh,*ef,*fc,*fh,*fe,l,r;

int main()
{
//freopen(“input.txt”,“r”,stdin);
cin>>P;
ll len=P.length();
c=(ll*)malloc(sizeof(ll)(len+1));
h=(ll
)malloc(sizeof(ll)(len+1));
e=(ll
)malloc(sizeof(ll)(len+1));
f=(ll
)malloc(sizeof(ll)*(len+1));
c=h=e=f=0;

``````ch=(ll*)malloc(sizeof(ll)*(len));
ce=(ll*)malloc(sizeof(ll)*(len));
cf=(ll*)malloc(sizeof(ll)*(len));

hc=(ll*)malloc(sizeof(ll)*(len));
he=(ll*)malloc(sizeof(ll)*(len));
hf=(ll*)malloc(sizeof(ll)*(len));

ec=(ll*)malloc(sizeof(ll)*(len));
eh=(ll*)malloc(sizeof(ll)*(len));
ef=(ll*)malloc(sizeof(ll)*(len));

fc=(ll*)malloc(sizeof(ll)*(len));
fh=(ll*)malloc(sizeof(ll)*(len));
fe=(ll*)malloc(sizeof(ll)*(len));

for(int loop=0;loop<len;loop++)
{
if(P[loop]=='c')
{
c[loop+1]=c[loop]+1;
h[loop+1]=h[loop];
e[loop+1]=e[loop];
f[loop+1]=f[loop];

}

else if(P[loop]=='h')
{
c[loop+1]=c[loop];
h[loop+1]=h[loop]+1;
e[loop+1]=e[loop];
f[loop+1]=f[loop];

}
else if(P[loop]=='e')
{
c[loop+1]=c[loop];
h[loop+1]=h[loop];
e[loop+1]=e[loop]+1;
f[loop+1]=f[loop];

}
else if(P[loop]=='f')
{
c[loop+1]=c[loop];
h[loop+1]=h[loop];
e[loop+1]=e[loop];
f[loop+1]=f[loop]+1;

}

}

/*for(int check=0;check<=len;check++)
{
cout<<"C:"<<c[check];
}
cout<<"\n";
for(int check=0;check<=len;check++)
{
cout<<"H:"<<h[check];
}
cout<<"\n";
for(int check=0;check<=len;check++)
{
cout<<"E:"<<e[check];
}
cout<<"\n";
for(int check=0;check<=len;check++)
{
cout<<"F:"<<f[check];
}
cout<<"\n";*/
``````

ch=ce=cf=hc=he=hf=ec=eh=ef=fh=fe=fc=0;
for(int loop=1;loop<len;loop++)
{
ch[loop]=(h[loop+1]-h[loop])*c[loop]+ch[loop-1];
ce[loop]=(e[loop+1]-e[loop])*c[loop]+ce[loop-1];
cf[loop]=(f[loop+1]-f[loop])*c[loop]+cf[loop-1];

``````        hc[loop]=(c[loop+1]-c[loop])*h[loop]+hc[loop-1];
he[loop]=(e[loop+1]-e[loop])*h[loop]+he[loop-1];
hf[loop]=(f[loop+1]-f[loop])*h[loop]+hf[loop-1];

ec[loop]=(c[loop+1]-c[loop])*e[loop]+ec[loop-1];
eh[loop]=(h[loop+1]-h[loop])*e[loop]+eh[loop-1];
ef[loop]=(f[loop+1]-f[loop])*e[loop]+ef[loop-1];

fc[loop]=(c[loop+1]-c[loop])*f[loop]+fc[loop-1];
fh[loop]=(h[loop+1]-h[loop])*f[loop]+fh[loop-1];
fe[loop]=(e[loop+1]-e[loop])*f[loop]+fe[loop-1];

}
``````

/* for(int check=0;check<len;check++)
{
cout<<“CH: “<<ch[check];
}
cout<<”\n”;
for(int check=0;check<len;check++)
{
cout<<“CE: “<<ce[check];
}
cout<<”\n”;
for(int check=0;check<len;check++)
{
cout<<“CF: “<<cf[check];
}
cout<<”\n”;*/
//cout<<"val "<<ec<<endl;
cin>>q;
while(q–)
{
cin>>a>>b;
cin>>l>>r;
if(P[l]==P[r] || l==r)
cout<<“0”<<endl;
else
{
if(a==‘c’&&b==‘f’)
cout<<cf[r-1]-(f[r]*c[l-1])<<endl;

``````        else if(a=='c'&&b=='e')
cout<<ce[r-1]-(e[r]*c[l-1])<<endl;

else if(a=='c'&&b=='h')
cout<<ch[r-1]-(h[r]*c[l-1])<<endl;

else if(a=='h'&&b=='c')
cout<<hc[r-1]-(c[r]*h[l-1])<<endl;

else if(a=='h'&&b=='e')
cout<<he[r-1]-(e[r]*h[l-1])<<endl;

else if(a=='h'&&b=='f')
cout<<hf[r-1]-(f[r]*h[l-1])<<endl;

else if(a=='e'&&b=='c')
cout<<ec[r-1]-(c[r]*e[l-1])<<endl;

else if(a=='e'&&b=='h')
cout<<eh[r-1]-(h[r]*e[l-1])<<endl;

else if(a=='e'&&b=='f')
cout<<ef[r-1]-(f[r]*e[l-1])<<endl;

else if(a=='f'&&b=='c')
cout<<fc[r-1]-(c[r]*f[l-1])<<endl;

else if(a=='f'&&b=='h')
cout<<fh[r-1]-(h[r]*f[l-1])<<endl;

else if(a=='f'&&b=='e')
cout<<fe[r-1]-(e[r]*f[l-1])<<endl;
}

}
free(c);free(h);free(e);free(f);free(ch);free(ce);free(cf);
free(hc);free(he);free(hf);free(ec);free(eh);free(ef);free(fc);free(fh);free(fe);
``````

return 0;
}

getting WA for 3rd subtask…please help correct my code http://www.codechef.com/viewsolution/7172102

This seems like a great long-challenge problem, and you gave fair warning with the “precalculation” tag.

The difficulty of getting the time down for full solution, gradually extending the precalc overhead, was just frustrating enough to make it interesting, with the benefit of seeing the restricted problem results move in the right direction. I ended up with a set of cumulative-pairs arrays calculated nicely in Python with:

``````# 1 where each character occurs
tuple( int(f == v) for f in full)
for v in cx )
# cumulative count of each character
cacc = tuple(
for v in cx )
# cumulative number of ordered pairs per ch. pair
pairsacc = tuple(
tuple(
for w in cx )
for v in cx )
``````

and calculation per query as described in the editorial. (`accumulate` is imported from `itertools` and `mul` from `operator`).

Hey pulkit the problem with your code was that you were initializing the large array of the order of 10^6 inside the main(which is a stack memory) , you should have done it outside the main(in heap memory) this resulted in SIGSEV . Also there was an overflow error which I corrected. Your AC solution
http://www.codechef.com/viewsolution/6297220 .

1 Like

Will you please elaborate?. If you mean as in this case we have only chef what if we have all 26 characters? than also I think we will move in the same direction. there will be 26*25 array.

Ya but in that case, the amount of memory usage will increase to 262510^6, which I think might give RE (SIGSEGV). Correct me if I am wrong. I just want to know is there a method which is more memory efficient with the similiar time complexity

1 Like

thanks a ton johri21. 