HackerRank How Many Substrings? Solution
In this post, we will solve HackerRank How Many Substrings? Problem Solution.
Consider a string of n characters, s, of where each character is indexed from 0 ton – 1. You are given q queries in the form of two integer indices: left and right. For each query, count and print the number of different substrings of $ in the inclusive range between left and right.
Note: Two substrings are different if their sequence of characters differs by at least one. For example, given the string s = aab, substrings 8[0,0]=a and 8[1,1] = a are the same but substrings $[0,1] = aa and $[1,2] = ab are different. | |
Input Format
The first line contains two space-separated integers describing the respective values of n
and q.
The second line contains a single string denoting s.
Each of the q subsequent lines contains two space-separated integers describing the respective values of left and right for a query.
Output Format
For each query, print the number of different substrings in the inclusive range between
index left and index right on a new line.
Sample Input 0
5 5 aabaa 1 1 1 4 1 1 1 4 0 2
Sample Output 0
1 8 1 8 5
Explanation 0
Given s = aabaa, we perform the following q = 5 queries:
1.1 1: The only substring of a is itself, so we print 1 on a new line.
- 1 4: The substrings of abaa are a, b, ab, ba, aa, aba, baa, and abaa, so we print 8 on a new line.
- 1 1: The only substring of a is itself, so we print 1 on a new line.
- 1 4: The substrings of abaa are a, b, ab, ba, aa, aba, baa, and abaa, so we print 8 on a new line.
5.0 2: The substrings of aab are a, b, aa, ab, and aab, so we print 5 on a new line.
How Many Substrings? C++ Solution
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define SZ(x) ((int)((x).size()))
#define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++)
#define per(i,j,k) for(int i=(int)j;i>=(int)k;i--)
using namespace std;
typedef long long LL;
typedef double DB;
const DB pi=acos(-1.0);
const int N=100005;
int go[N<<1][26],fail[N<<1],len[N<<1],tot,last;
int n;
int cnt=0;
namespace seg{
int tag[N<<2];
LL sum[N<<2];
inline void Tag(int me,int l,int r,int v){
tag[me]+=v;
sum[me]+=(r-l+1)*1ll*v;
}
inline void down(int me,int l,int r){
if(tag[me]==0)return;
int mid=(l+r)>>1;
Tag(me<<1,l,mid,tag[me]);
Tag(me<<1|1,mid+1,r,tag[me]);
tag[me]=0;
}
void add(int me,int l,int r,int x,int y,int v){
//if(l==1&&r==n)printf("add %d %d %d\n",x,y,v);
if(l^r)down(me,l,r);
if(x<=l&&r<=y){
Tag(me,l,r,v);
return;
}
int mid=(l+r)>>1;
if(x<=mid)add(me<<1,l,mid,x,y,v);
if(y>mid)add(me<<1|1,mid+1,r,x,y,v);
sum[me]=sum[me<<1]+sum[me<<1|1];
}
LL ask(int me,int l,int r,int x,int y){
if(l^r)down(me,l,r);
if(x<=l&&r<=y)return sum[me];
int mid=(l+r)>>1;
LL ret=0;
if(x<=mid)ret+=ask(me<<1,l,mid,x,y);
if(y>mid)ret+=ask(me<<1|1,mid+1,r,x,y);
return ret;
}
void Do(int pre,int now,int L,int R){
if(L>R)return;
++cnt;
//printf("_%d %d %d %d\n",pre,now,L,R);
if(pre)add(1,1,n,pre-R+1,pre-L+1,-1);
add(1,1,n,now-R+1,now-L+1,1);
}
};
namespace lct{
int l[N<<2],r[N<<2],fa[N<<2];
int last[N<<2];
inline bool top(int x){return (!fa[x])||(l[fa[x]]!=x&&r[fa[x]]!=x);}
inline void left(int x){
int y=fa[x];int z=fa[y];
r[y]=l[x];if(l[x])fa[l[x]]=y;
fa[x]=z;if(l[z]==y)l[z]=x;else if(r[z]==y)r[z]=x;
l[x]=y;fa[y]=x;
}
inline void right(int x){
int y=fa[x];int z=fa[y];
l[y]=r[x];if(r[x])fa[r[x]]=y;
fa[x]=z;if(l[z]==y)l[z]=x;else if(r[z]==y)r[z]=x;
r[x]=y;fa[y]=x;
}
inline void down(int x){
if(l[x])last[l[x]]=last[x];
if(r[x])last[r[x]]=last[x];
}
int q[N<<2];
inline void splay(int x){
q[q[0]=1]=x;
for(int k=x;!top(k);k=fa[k])q[++q[0]]=fa[k];
per(i,q[0],1)down(q[i]);
while(!top(x)){
int y=fa[x];int z=fa[y];
if(top(y)){
if(l[y]==x)right(x);else left(x);
}
else{
if(r[z]==y){
if(r[y]==x)left(y),left(x);
else right(x),left(x);
}
else{
if(l[y]==x)right(y),right(x);
else left(x),right(x);
}
}
}
}
void Access(int x,int cov){
int y=0;
for(;x;y=x,x=fa[x]){
splay(x);
down(x);
r[x]=0;
int L,R;
int z=x;
while(l[z])z=l[z];
L=len[fail[z]]+1;
splay(z);splay(x);
z=x;
while(r[z])z=r[z];
R=len[z];
splay(z);splay(x);
seg::Do(last[x],cov,L,R);
r[x]=y;
last[x]=cov;
}
}
void SetFa(int x,int y,int po){
fa[x]=y;
Access(x,po);
}
void split(int x,int y,int d){
splay(y);
down(y);
r[y]=0;
fa[d]=y;
splay(x);
fa[x]=d;
last[d]=last[x];
}
};
namespace sam{
void init(){
tot=last=1;
}
void expended(int x,int po){
int gt=++tot;len[gt]=len[last]+1;int p=last;last=tot;
for(;p&&(!go[p][x]);p=fail[p])go[p][x]=gt;
if(!p){
fail[gt]=1;
lct::SetFa(gt,1,po);
return;
}
int xx=go[p][x];
if(len[xx]==len[p]+1){
fail[gt]=xx;
lct::SetFa(gt,xx,po);
return;
}
int tt=++tot;
len[tt]=len[p]+1;
fail[tt]=fail[xx];
int dt=fail[xx];
fail[xx]=fail[gt]=tt;
lct::split(xx,dt,tt);
lct::SetFa(gt,tt,po);
rep(i,0,25)go[tt][i]=go[xx][i];
for(;p&&(go[p][x]==xx);p=fail[p])go[p][x]=tt;
}
};
int Q;
char str[N];
int qL[N];
vector<int>que[N];
LL ans[N];
void Main(){
rep(i,1,n){
sam::expended(str[i]-'a',i);
rep(j,0,que[i].size()-1){
int id=que[i][j];
ans[id]=seg::ask(1,1,n,qL[id],n);
}
}
}
void init(){
scanf("%d%d",&n,&Q);
scanf("%s",str+1);
rep(i,1,Q){
int r;scanf("%d%d",&qL[i],&r);
qL[i]++;r++;
que[r].pb(i);
}
sam::init();
}
void Output(){
rep(i,1,Q)printf("%lld\n",ans[i]);
}
int main(){
init();
Main();
Output();
return 0;
}
How Many Substrings? C Sharp Solution
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
class Solution {
static void Main(String[] args) {
string[] tokens_n = Console.ReadLine().Split(' ');
int n = Convert.ToInt32(tokens_n[0]);
int q = Convert.ToInt32(tokens_n[1]);
string s = Console.ReadLine();
var queries = new Query[q];
for(int a0 = 0; a0 < q; a0++){
string[] tokens_left = Console.ReadLine().Split(' ');
queries[a0] = new Query(Convert.ToInt32(tokens_left[0]), Convert.ToInt32(tokens_left[1]));
}
SubstringCounter.solve(s.ToCharArray(), queries);
foreach (Query e in queries)
Console.WriteLine(e.Answer);
}
}
public class SubstringCounter
{
public static void solve(char[] str, Query[] queries)
{
int[] suffixArray = SuffixArray.BuildSuffixArray(str);
int[] suffixArrayInverse = new int[str.Length];
for (int i = 0; i < str.Length; i++)
{
suffixArrayInverse[suffixArray[i]] = i;
}
int[] lcp = SuffixArray.BuildLcpKasai(suffixArray, suffixArrayInverse, str);
//defaultdict instead of List?
var queriesIndexedByLeft = new List<Query>[str.Length];
for (int i = 0; i < str.Length; i++)
queriesIndexedByLeft[i] = new List<Query>();
foreach (Query query in queries)
queriesIndexedByLeft[query.Left].Add(query);
var maximumPrefixContainingInterval = LcpIntervalHelper.BuildIntervals(lcp);
SubstringSum substringSum = new SubstringSum(str.Length);
for (int i = str.Length - 1; i >= 0; i--)
{
//No ValueTuples in hacker rank :o(
Stack<Tuple<int, int>> tups = LcpIntervalHelper.GetLastSeen(i,
maximumPrefixContainingInterval, suffixArrayInverse);
int matchedLcp = 0;
while (tups.Count > 0)
{
var tup = tups.Pop();
int j = tup.Item1;
int newLcp = tup.Item2;
substringSum.addRange(j + matchedLcp, j + newLcp - 1);
matchedLcp = newLcp;
}
foreach (Query query in queriesIndexedByLeft[i])
{
query.Answer = substringSum.getSum(query.Left, query.Right);
}
}
}
}
public class Query
{
public int Left { get; }
public int Right { get; }
public long Answer { get; set; }
public Query(int left, int right)
{
this.Left = left;
this.Right = right;
}
}
//When we want count substrings whilst we change the right value of a string for a prebuilt suffix array the standard
//Lcp array is less helpful as it is not ordered in terms of the original string and will become full of holes. With holes
//we would need to consider the minimum of all intervening missing LCP elements to calculate the LCP between two elements.
//
//So instead we flip the idea on its side to have a set of LcpIntervals. Each lcp interval contains all Suffixes in
//the Suffix Array with a minimum lcp.
//
//At least the minimum LCP of all but the first element in the interval can be used to reduce the substring count.
//For each Suffix Array item we only store the LcpInterval with the maximum MinimumLcp. This ensures at most n
//intervals.
//So as the right value of a query changes the problem becomes how to spot if this is the first suffix in the
//interval or not.
//We do this by processing the string in reverse order and noting the leftmost time a string in the interval is seen.
// Each LcpInterval contains a link to its enclosing superinterval, each interval will be enclosed by one and only
// one Super-Interval, apart from the root.
// I suppose you could consider it to be a tree and perhaps I should have researched if an appropriate standard
// pattern / data structure existed.
// The curious thing is that when I look at the finished working result I notice that Left and Right of LcpInterval
// don't actually appear to be needed. But if I remove them the motivating idea of the code would be impossible to
// understand. I will leave them in as comments, with the caveat of all comments that as they don't
// affect the correct working of the code they may be wrong.
public class LcpIntervalHelper
{
public class LcpInterval
{
//public int Left { get; }
//public int Right { get; set; }
public LcpInterval SuperLcpInterval { get; set; }
public int MinimumLcp { get; }
public int LastSeen { get; set; }
//public LcpInterval(int length, int left, int right, LcpInterval superLcpInterval)
public LcpInterval(int length, LcpInterval superLcpInterval)
{
MinimumLcp = length;
//Left = left;
//Right = right;
SuperLcpInterval = superLcpInterval;
LastSeen = int.MaxValue;
}
public override string ToString()
{
return
// $"{nameof(Left)}: {Left}, " +
// $"{nameof(Right)}: {Right}" +
$"{nameof(MinimumLcp)}: {MinimumLcp} " +
$"{nameof(LastSeen)}: {LastSeen} ";
}
}
public static Stack<Tuple<int, int>> GetLastSeen(int i, LcpInterval[] lcpIntervals, int[] saInverse)
{
var interval = lcpIntervals[saInverse[i]];
var ret = new Stack<Tuple<int, int>>();
int lastSeen = int.MaxValue;
while (interval.SuperLcpInterval != null)
{
if (interval.LastSeen < lastSeen)
{
ret.Push(new Tuple<int, int>(interval.LastSeen, interval.MinimumLcp));
lastSeen = interval.LastSeen;
}
interval.LastSeen = i;
interval = interval.SuperLcpInterval;
}
return ret;
}
public static LcpInterval[] BuildIntervals(int[] lcp)
{
// the root interval should be the only one with parent == null
//var interval = new LcpInterval(0, 0, lcp.Length , null);
var interval = new LcpInterval(0, null);
var suffixArrayIntervals = new LcpInterval[lcp.Length];
//Step through lcp build interval assigning max count interval to each item of the initial string.
//Each interval has a link to the containit interval.
for (int i = 0; i < lcp.Length - 1; i++)
{
//start off we are in the current interval
//We know we are in the same interval as indexes to the left. We might be in
//a sub interval if we have a higher matching prefex to the right.
suffixArrayIntervals[i] = interval;
LcpInterval oldLcpInterval = interval;
while (interval.MinimumLcp > lcp[i + 1] && interval.SuperLcpInterval != null)
{
//close the current ineterval
// interval.Right = i;
oldLcpInterval = interval;
interval = interval.SuperLcpInterval;
}
if (interval.MinimumLcp < lcp[i + 1])
{
if (interval != oldLcpInterval)
{
// We have closed a higher LCP interval but are inserting a new one between it and its previous
// SuperInterval.
// e.g. oldInterval was MinimumLCP = 2 with SuperInterval MinimumLCP=0
// but we now whish to open an interval MinimumLCP=1
//interval = new LcpInterval(lcp[i + 1], oldLcpInterval.Left, lcp.Length - 1, interval);
interval = new LcpInterval(lcp[i + 1], interval);
oldLcpInterval.SuperLcpInterval = interval;
}
else
{
// Starting a new interval from the previous interval with higher LCP
//interval = new LcpInterval(lcp[i + 1], i, lcp.Length - 1, interval);
interval = new LcpInterval(lcp[i + 1], interval);
suffixArrayIntervals[i] = interval;
}
}
}
suffixArrayIntervals[lcp.Length - 1] = interval;
return suffixArrayIntervals;
}
}
// For the Hackerrank problem we are interested in how the matching SuffixArray LCP values to reduce the number
// of substrings
//
// Without matching LCP prefixes the number of substrings is n=(right+1-left)(right+2-left)/2
// This is just math for SUM(i=left to right){i}. This is clearly Trivial to calculate.
//
// So we are only really interested in LCP values and when they should be used to reduce substring count.
// When we have a matching prefix in SuffixArray index j > i. The potential to use those matching prefixes
// drops off as the j extends further to the right i.e j+lcp > right.
// So naively we want
// for (int k = j; j < j+lcp; j++)
// {
// OffsetSum.UpdateValue(k, 1);
// }
// And we can use OffsetSum to reduce the number of Substrings as we would normally for an LCP/SuffixArray
// However if lcp is > 4 potentially much > 4 this becomes slow.
// So we simulate this by using a math formula
// And calculate the sum using k * LinearSum(k) + OffsetSum(k)
//
// For instance for n = 13
// left =3 right =10 i.e Offset = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 , 0, 0]
//
// Can be simulated with
// Offset = [0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0 ]
// Linear = [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0 ]
//
// Sum( 1 ) = SumOffset(1) + SumLinear(1) = 0 + 1*0 = 0
// Sum( 3 ) = SumOffset(3) + SumLinear(3) = -2 + 3*1 = 1
// Sum( 4 ) = SumOffset(4) + SumLinear(4) = -2 + 4*1 = 2
// Sum( 5 ) = SumOffset(5) + SumLinear(5) = -2+ 5*1 = 3
//
// Sum( 8 ) = SumOffset(8) + SumLinear(8) = -2 + 8*1 = 6
// Sum( 9 ) = SumOffset(9) + SumLinear(9) = -2 + 9*1 = 7
// Sum( 10 ) = SumOffset(10) + SumLinear(10) = 8 + 10*0 = 8
// Sum( 11 ) = SumOffset(11) + SumLinear(11) = 8 + 11*0 = 8
// Sum( 12 ) = SumOffset(8) + SumLinear(8) = 8 + 12*0 = 8
public class SubstringSum
{
class FenwickTree
{
private long[] fenwickArray;
public FenwickTree(int n)
{
fenwickArray = new long[n];
}
public void UpdateValue(int index, long value)
{
while (index < fenwickArray.Length)
{
fenwickArray[index] += value;
index += LSB(index + 1);
}
}
private int LSB(int i)
{
return i & -i;
}
public long GetValue(int i)
{
long sum = 0;
while (i != 0)
{
sum += fenwickArray[i - 1];
i -= LSB(i);
}
return sum;
}
}
private FenwickTree LinearSum;
private FenwickTree OffsetSum;
private int n;
public SubstringSum(int n)
{
this.n = n;
LinearSum = new FenwickTree(n);
OffsetSum = new FenwickTree(n);
}
public void add(int i, long v)
{
if (i < n)
{
OffsetSum.UpdateValue(i, v);
}
}
public void addRange(int left, int right)
{
LinearSum.UpdateValue(left, 1);
OffsetSum.UpdateValue(left, 1 - left);
LinearSum.UpdateValue(right, -1);
OffsetSum.UpdateValue(right, right);
}
public long getSum(int i)
{
long li = i;
return li * LinearSum.GetValue(i + 1) + OffsetSum.GetValue(i + 1);
}
//inclusive right
public long getSum(int left, int right)
{
// I don't understand the rules of implicit type conversions in intermediate calculations.
// so better safe than sorry. 100k^2 is too big for an int.
long rightlong = right;
long leftlong = left;
return (rightlong + 1 - leftlong) * (rightlong + 2 - leftlong) / 2 - getSum(right);
}
}
public static class SuffixArray
{
//Naive c# sort LCP generation for test10 n=100,000 was 973 ms
//This Kasai trick bought it down to 6 ms
//The simple idea is that if a suffix matches k characters. The suffix generated by one character further along
//in the string must match at least k-1 characters, i.e. just use the previous matching prefix one char
//further along.
//So we don't need to check the first k-1 chars. This gives us an O(n) sort.
public static int[] BuildLcpKasai(int[] suffixArray, int[] suffixArrayInverse, char[] str)
{
int[] lcp = new int[suffixArray.Length];
int n = suffixArray.Length;
int k = 0;
for (int i = 0; i < n; i++)
{
int inversei = suffixArrayInverse[i];
if (inversei == 0)
{
k = 0;
continue;
}
int j = suffixArray[inversei - 1];
while (i + k < n && j + k < n && str[i + k] == str[j + k])
k++;
lcp[inversei] = k;
if (k > 0)
k--;
}
return lcp;
}
// Naive SuffixArray creation using CSharp Collection.sort was too slow approx 6 seconds for 100k
// SO I used a more efficient algorithm
// Code adapted from:
// http://www.cs.cmu.edu/afs/cs/academic/class/15492-f07/www/handouts/ks-icalp03.pdf
// Juha Karkkainen and Peter Sanders
public static int[] BuildSuffixArray(char[] str)
{
int[] s = new int[str.Length + 3];
int[] SA = new int[str.Length];
for (int i = 0; i < str.Length; i++)
s[i] = str[i];
s[str.Length] = 0;
s[str.Length + 1] = 0;
s[str.Length + 2] = 0;
suffixArray(s, SA, str.Length, 128);
return SA;
}
static int GetI(int[] SA12, int n0, int t)
{
return (SA12[t] < n0 ? SA12[t] * 3 + 1 : (SA12[t] - n0) * 3 + 2);
}
static bool leq(int a1, int a2, int b1, int b2)
{
// lexic. order for pairs
return (a1 < b1 || a1 == b1 && a2 <= b2);
} // and triples
static bool leq(int a1, int a2, int a3, int b1, int b2, int b3)
{
return (a1 < b1 || a1 == b1 && leq(a2, a3, b2, b3));
}
// stably sort a[0..n-1] to b[0..n-1] with keys in 0..K from r
static void radixPass(int[] a, int[] b, int[] r, int rOffset, int n, int K)
{
// count occurrences
int[] c = new int[K + 1]; // counter array
for (int i = 0; i <= K; i++) c[i] = 0; // reset counters
for (int i = 0; i < n; i++) c[r[rOffset + a[i]]]++; // count occurences
for (int i = 0, sum = 0; i <= K; i++)
{
// exclusive prefix sums
int t = c[i];
c[i] = sum;
sum += t;
}
for (int i = 0; i < n; i++) b[c[r[rOffset + a[i]]]++] = a[i]; // sort
}
// find the suffix array SA of s[0..n-1] in {1..K}^n
// require s[n]=s[n+1]=s[n+2]=0, n>=2
static void suffixArray(int[] s, int[] SA, int n, int K)
{
int n0 = (n + 2) / 3, n1 = (n + 1) / 3, n2 = n / 3, n02 = n0 + n2;
int[] s12 = new int[n02 + 3];
s12[n02] = s12[n02 + 1] = s12[n02 + 2] = 0;
int[] SA12 = new int[n02 + 3];
SA12[n02] = SA12[n02 + 1] = SA12[n02 + 2] = 0;
int[] s0 = new int[n0];
int[] SA0 = new int[n0];
// generate positions of mod 1 and mod 2 suffixes
// the "+(n0-n1)" adds a dummy mod 1 suffix if n%3 == 1
for (int i = 0, j = 0; i < n + (n0 - n1); i++)
if (i % 3 != 0)
s12[j++] = i;
// lsb radix sort the mod 1 and mod 2 triples
radixPass(s12, SA12, s, 2, n02, K);
radixPass(SA12, s12, s, 1, n02, K);
radixPass(s12, SA12, s, 0, n02, K);
int name = 0, c0 = -1, c1 = -1, c2 = -1;
for (int i = 0; i < n02; i++)
{
if (s[SA12[i]] != c0 || s[SA12[i] + 1] != c1 || s[SA12[i] + 2] != c2)
{
name++;
c0 = s[SA12[i]];
c1 = s[SA12[i] + 1];
c2 = s[SA12[i] + 2];
}
if (SA12[i] % 3 == 1)
{
s12[SA12[i] / 3] = name;
} // left half
else
{
s12[SA12[i] / 3 + n0] = name;
} // right half
}
if (name < n02)
{
suffixArray(s12, SA12, n02, name);
// store unique names in s12 using the suffix array
for (int i = 0; i < n02; i++) s12[SA12[i]] = i + 1;
}
else // generate the suffix array of s12 directly
for (int i = 0; i < n02; i++)
SA12[s12[i] - 1] = i;
// stably sort the mod 0 suffixes from SA12 by their first character
for (int i = 0, j = 0; i < n02; i++)
if (SA12[i] < n0)
s0[j++] = 3 * SA12[i];
radixPass(s0, SA0, s, 0, n0, K);
// merge sorted SA0 suffixes and sorted SA12 suffixes
for (int p = 0, t = n0 - n1, k = 0; k < n; k++)
{
int i = GetI(SA12, n0, t); // pos of current offset 12 suffix
int j = SA0[p]; // pos of current offset 0 suffix
if (SA12[t] < n0
? leq(s[i], s12[SA12[t] + n0], s[j], s12[j / 3])
: leq(s[i], s[i + 1], s12[SA12[t] - n0 + 1], s[j], s[j + 1], s12[j / 3 + n0]))
{
// suffix from SA12 is smaller
SA[k] = i;
t++;
if (t == n02)
{
// done --- only SA0 suffixes left
for (k++; p < n0; p++, k++) SA[k] = SA0[p];
}
}
else
{
SA[k] = j;
p++;
if (p == n0)
{
// done --- only SA12 suffixes left
for (k++; t < n02; t++, k++) SA[k] = GetI(SA12, n0, t);
}
}
}
}
}
How Many Substrings? Java Solution
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.InputMismatchException;
import java.util.List;
public class How_Many_Substrings {
InputStream is;
PrintWriter out;
String INPUT = "";
void solve()
{
int n = ni(), Q = ni();
char[] s = ns(n);
int[][] qs = new int[Q][];
for(int z = 0;z < Q;z++){
qs[z] = new int[]{ni(), ni(), z};
}
Arrays.sort(qs, new Comparator<int[]>() {
public int compare(int[] a, int[] b) {
return a[1] - b[1];
}
});
SuffixAutomatonOfBit sa = SuffixAutomatonOfBit.build(s);
sa.sortTopologically();
SuffixAutomatonOfBit.Node[] nodes = sa.nodes;
int[] from = new int[nodes.length-1];
int[] to = new int[nodes.length-1];
int p = 0;
for(SuffixAutomatonOfBit.Node node : nodes){
if(node.id >= 1){
from[p] = node.link.id; to[p] = node.id; p++;
}
}
assert p == nodes.length-1;
int[][] g = packU(nodes.length, from, to);
int[][] pars = parents3(g, 0);
int[] par = pars[0], ord = pars[1], dep = pars[2];
HeavyLightDecomposition hld = new HeavyLightDecomposition(g, par, ord, dep);
int m = hld.cluspath.length;
SegmentTreeOverwrite[] sts = new SegmentTreeOverwrite[m];
for(int i = 0;i < m;i++){
sts[i] = new SegmentTreeOverwrite(hld.cluspath[i].length);
}
int[] base = new int[n];
int qp = 0;
int np = 0;
long[] ft0 = new long[n+3];
long[] ft1 = new long[n+3];
long[] ans = new long[Q];
for(int i = 0;i < n;i++){
while(!(nodes[np].len == i+1 && nodes[np].original == null))np++;
base[i] = np;
// tr("base", base[i]);
// 5 3 1 0
// 5 3 1 0
// 8 6 3 1 0 ?
// aaba
// delete
int cur = 0;
int ppos = 0;
while(true){
int last = sts[hld.clus[cur]].get(hld.clusiind[cur]);
if(last == -1)break;
int lca = hld.lca(base[last], base[i]);
// delete from lca to cur
// nodes[cur].len, nodes[lca].len;
int inf = last-nodes[lca].len+1;
int sup = last-ppos+1;
// tr("del", last, ppos, nodes[lca].len, inf, sup);
// _/
addFenwick(ft0, 0, -(sup-inf));
addFenwick(ft0, sup+1, +(sup-inf));
addFenwick(ft0, inf+1, -(inf+1));
addFenwick(ft0, sup+1, +inf+1);
addFenwick(ft1, inf+1, 1);
addFenwick(ft1, sup+1, -1);
// tr(i, restoreRangeFenwick(ft0, ft1));
ppos = nodes[lca].len;
assert dep[base[i]]-dep[lca]-1 >= 0;
cur = hld.ancestor(base[i], dep[base[i]]-dep[lca]-1);
}
// x
//b a
// a
// paint
int cx = hld.clus[base[i]]; // cluster
int ind = hld.clusiind[base[i]]; // pos in cluster
while(true){
sts[cx].update(0, ind+1, i);
int con = par[hld.cluspath[cx][0]];
if(con == -1)break;
ind = hld.clusiind[con];
cx = hld.clus[con];
}
// tr(i, restoreRangeFenwick(ft0, ft1));
addFenwick(ft0, 0, i+1+1);
addFenwick(ft0, i+1+1, -(i+1+1));
addFenwick(ft1, 0, -1);
addFenwick(ft1, i+1+1, 1);
// tr(i, restoreRangeFenwick(ft0, ft1));
while(qp < Q && qs[qp][1] <= i){
// tr(qs[qp]);
ans[qs[qp][2]] = sumRangeFenwick(ft0, ft1, qs[qp][0]);
qp++;
}
}
for(long an : ans){
out.println(an);
}
}
public static long sumFenwick(long[] ft, int i)
{
long sum = 0;
for(i++;i > 0;i -= i&-i)sum += ft[i];
return sum;
}
public static void addFenwick(long[] ft, int i, long v)
{
if(v == 0)return;
int n = ft.length;
for(i++;i < n;i += i&-i)ft[i] += v;
}
public static int firstGEFenwick(long[] ft, long v)
{
int i = 0, n = ft.length;
for(int b = Integer.highestOneBit(n);b != 0;b >>= 1){
if((i|b) < n && ft[i|b] < v){
i |= b;
v -= ft[i];
}
}
return i;
}
public static long[] restoreFenwick(long[] ft)
{
int n = ft.length-1;
long[] ret = new long[n];
for(int i = 0;i < n;i++)ret[i] = sumFenwick(ft, i);
for(int i = n-1;i >= 1;i--)ret[i] -= ret[i-1];
return ret;
}
public static int findGFenwick(long[] ft, long v)
{
int i = 0;
int n = ft.length;
for(int b = Integer.highestOneBit(n);b != 0 && i < n;b >>= 1){
if(i + b < n){
int t = i + b;
if(v >= ft[t]){
i = t;
v -= ft[t];
}
}
}
return v != 0 ? -(i+1) : i-1;
}
public static long[] buildFenwick(long[] a)
{
int n = a.length;
long[] ft = new long[n+1];
System.arraycopy(a, 0, ft, 1, n);
for(int k = 2, h = 1;k <= n;k*=2, h*=2){
for(int i = k;i <= n;i+=k){
ft[i] += ft[i-h];
}
}
return ft;
}
public static void addRangeFenwick(long[] ft0, long[] ft1, int i, long v)
{
addFenwick(ft1, i+1, -v);
addFenwick(ft1, 0, v);
addFenwick(ft0, i+1, v*(i+1));
}
public static void addRangeFenwick(long[] ft0, long[] ft1, int a, int b, long v)
{
if(a <= b){
addFenwick(ft1, b+1, -v);
addFenwick(ft0, b+1, v*(b+1));
addFenwick(ft1, a, v);
addFenwick(ft0, a, -v*a);
}
}
public static long sumRangeFenwick(long[] ft0, long[] ft1, int i)
{
return sumFenwick(ft1, i) * (i+1) + sumFenwick(ft0, i);
}
public static long[] restoreRangeFenwick(long[] ft0, long[] ft1)
{
int n = ft0.length-1;
long[] ret = new long[n];
for(int i = 0;i < n;i++)ret[i] = sumRangeFenwick(ft0, ft1, i);
for(int i = n-1;i >= 1;i--)ret[i] -= ret[i-1];
return ret;
}
public static class SegmentTreeOverwrite {
public int M, H, N;
public int[] cover;
public int I = Integer.MAX_VALUE;
public SegmentTreeOverwrite(int len)
{
N = len;
M = Integer.highestOneBit(Math.max(N-1, 1))<<2;
H = M>>>1;
cover = new int[M];
Arrays.fill(cover, I);
for(int i = 0;i < N;i++){
cover[H+i] = -1;
}
for(int i = H-1;i >= 1;i--){
propagate(i);
}
}
private void propagate(int i){}
public void update(int l, int r, int v){ update(l, r, v, 0, H, 1); }
private void update(int l, int r, int v, int cl, int cr, int cur)
{
if(l <= cl && cr <= r){
cover[cur] = v;
propagate(cur);
}else{
int mid = cl+cr>>>1;
if(cover[cur] != I){ // back-propagate
cover[2*cur] = cover[2*cur+1] = cover[cur];
cover[cur] = I;
propagate(2*cur);
propagate(2*cur+1);
}
if(cl < r && l < mid){
update(l, r, v, cl, mid, 2*cur);
}
if(mid < r && l < cr){
update(l, r, v, mid, cr, 2*cur+1);
}
propagate(cur);
}
}
public int get(int x){
int val = I;
for(int i = H+x;i >= 1;i>>>=1){
if(cover[i] != I)val = cover[i];
}
return val;
}
}
public static class HeavyLightDecomposition {
public int[] clus;
public int[][] cluspath;
public int[] clusiind;
public int[] par, dep;
public HeavyLightDecomposition(int[][] g, int[] par, int[] ord, int[] dep)
{
init(g, par, ord, dep);
}
public void init(int[][] g, int[] par, int[] ord, int[] dep)
{
clus = decomposeToHeavyLight(g, par, ord);
cluspath = clusPaths(clus, ord);
clusiind = clusIInd(cluspath, g.length);
this.par = par;
this.dep = dep;
}
public static int[] decomposeToHeavyLight(int[][] g, int[] par, int[] ord)
{
int n = g.length;
int[] size = new int[n];
Arrays.fill(size, 1);
for(int i = n-1;i > 0;i--)size[par[ord[i]]] += size[ord[i]];
int[] clus = new int[n];
Arrays.fill(clus, -1);
int p = 0;
for(int i = 0;i < n;i++){
int u = ord[i];
if(clus[u] == -1)clus[u] = p++;
// centroid path (not heavy path)
int argmax = -1;
for(int v : g[u]){
if(par[u] != v && (argmax == -1 || size[v] > size[argmax]))argmax = v;
}
if(argmax != -1)clus[argmax] = clus[u];
}
return clus;
}
public static int[][] clusPaths(int[] clus, int[] ord)
{
int n = clus.length;
int[] rp = new int[n];
int sup = 0;
for(int i = 0;i < n;i++){
rp[clus[i]]++;
sup = Math.max(sup, clus[i]);
}
sup++;
int[][] row = new int[sup][];
for(int i = 0;i < sup;i++)row[i] = new int[rp[i]];
for(int i = n-1;i >= 0;i--){
row[clus[ord[i]]][--rp[clus[ord[i]]]] = ord[i];
}
return row;
}
public static int[] clusIInd(int[][] clusPath, int n)
{
int[] iind = new int[n];
for(int[] path : clusPath){
for(int i = 0;i < path.length;i++){
iind[path[i]] = i;
}
}
return iind;
}
public int lca(int x, int y)
{
int rx = cluspath[clus[x]][0];
int ry = cluspath[clus[y]][0];
while(clus[x] != clus[y]){
if(dep[rx] > dep[ry]){
x = par[rx];
rx = cluspath[clus[x]][0];
}else{
y = par[ry];
ry = cluspath[clus[y]][0];
}
}
return clusiind[x] > clusiind[y] ? y : x;
}
public int ancestor(int x, int v)
{
while(x != -1){
if(v <= clusiind[x])return cluspath[clus[x]][clusiind[x]-v];
v -= clusiind[x]+1;
x = par[cluspath[clus[x]][0]];
}
return x;
}
}
public static int lca2(int a, int b, int[][] spar, int[] depth) {
if (depth[a] < depth[b]) {
b = ancestor(b, depth[b] - depth[a], spar);
} else if (depth[a] > depth[b]) {
a = ancestor(a, depth[a] - depth[b], spar);
}
if (a == b)
return a;
int sa = a, sb = b;
for (int low = 0, high = depth[a], t = Integer.highestOneBit(high), k = Integer
.numberOfTrailingZeros(t); t > 0; t >>>= 1, k--) {
if ((low ^ high) >= t) {
if (spar[k][sa] != spar[k][sb]) {
low |= t;
sa = spar[k][sa];
sb = spar[k][sb];
} else {
high = low | t - 1;
}
}
}
return spar[0][sa];
}
protected static int ancestor(int a, int m, int[][] spar) {
for (int i = 0; m > 0 && a != -1; m >>>= 1, i++) {
if ((m & 1) == 1)
a = spar[i][a];
}
return a;
}
public static int[][] logstepParents(int[] par) {
int n = par.length;
int m = Integer.numberOfTrailingZeros(Integer.highestOneBit(n - 1)) + 1;
int[][] pars = new int[m][n];
pars[0] = par;
for (int j = 1; j < m; j++) {
for (int i = 0; i < n; i++) {
pars[j][i] = pars[j - 1][i] == -1 ? -1 : pars[j - 1][pars[j - 1][i]];
}
}
return pars;
}
public static int[][] parents3(int[][] g, int root) {
int n = g.length;
int[] par = new int[n];
Arrays.fill(par, -1);
int[] depth = new int[n];
depth[0] = 0;
int[] q = new int[n];
q[0] = root;
for (int p = 0, r = 1; p < r; p++) {
int cur = q[p];
for (int nex : g[cur]) {
if (par[cur] != nex) {
q[r++] = nex;
par[nex] = cur;
depth[nex] = depth[cur] + 1;
}
}
}
return new int[][] { par, q, depth };
}
static int[][] packU(int n, int[] from, int[] to) {
int[][] g = new int[n][];
int[] p = new int[n];
for (int f : from)
p[f]++;
for (int t : to)
p[t]++;
for (int i = 0; i < n; i++)
g[i] = new int[p[i]];
for (int i = 0; i < from.length; i++) {
g[from[i]][--p[from[i]]] = to[i];
g[to[i]][--p[to[i]]] = from[i];
}
return g;
}
public static class SuffixAutomatonOfBit {
public Node t0;
public int len;
public Node[] nodes;
public int gen;
private boolean sortedTopologically = false;
private boolean lexsorted = false;
private SuffixAutomatonOfBit(int n)
{
gen = 0;
nodes = new Node[2*n];
this.t0 = makeNode(0, null);
}
private Node makeNode(int len, Node original)
{
Node node = new Node();
node.id = gen;
node.original = original;
node.len = len;
nodes[gen++] = node;
return node;
}
public static class Node
{
public int id;
public int len;
public char key;
public Node link;
private Node[] next = new Node[3];
public Node original;
public int np = 0;
public int hit = 0;
public void putNext(char c, Node to)
{
to.key = c;
if(hit<<~(c-'a')<0){
for(int i = 0;i < np;i++){
if(next[i].key == c){
next[i] = to;
return;
}
}
}
hit |= 1<<c-'a';
if(np == next.length){
next = Arrays.copyOf(next, np*2);
}
next[np++] = to;
}
public boolean containsKeyNext(char c)
{
return hit<<~(c-'a')<0;
// for(int i = 0;i < np;i++){
// if(next[i].key == c)return true;
// }
// return false;
}
public Node getNext(char c)
{
if(hit<<~(c-'a')<0){
for(int i = 0;i < np;i++){
if(next[i].key == c)return next[i];
}
}
return null;
}
public List<String> suffixes(char[] s)
{
List<String> list = new ArrayList<String>();
if(id == 0)return list;
int first = original != null ? original.len : len;
for(int i = link.len + 1;i <= len;i++){
list.add(new String(s, first - i, i));
}
return list;
}
}
public static SuffixAutomatonOfBit build(char[] str)
{
int n = str.length;
SuffixAutomatonOfBit sa = new SuffixAutomatonOfBit(n);
sa.len = str.length;
Node last = sa.t0;
for(char c : str){
last = sa.extend(last, c);
}
return sa;
}
public Node extend(Node last, char c)
{
Node cur = makeNode(last.len+1, null);
Node p;
for(p = last; p != null && !p.containsKeyNext(c);p = p.link){
p.putNext(c, cur);
}
if(p == null){
cur.link = t0;
}else{
Node q = p.getNext(c); // not null
if(p.len + 1 == q.len){
cur.link = q;
}else{
Node clone = makeNode(p.len+1, q);
clone.next = Arrays.copyOf(q.next, q.next.length);
clone.hit = q.hit;
clone.np = q.np;
clone.link = q.link;
for(;p != null && q.equals(p.getNext(c)); p = p.link){
p.putNext(c, clone);
}
q.link = cur.link = clone;
}
}
return cur;
}
public SuffixAutomatonOfBit lexSort()
{
for(int i = 0;i < gen;i++){
Node node = nodes[i];
Arrays.sort(node.next, 0, node.np, new Comparator<Node>() {
public int compare(Node a, Node b) {
return a.key - b.key;
}
});
}
lexsorted = true;
return this;
}
public SuffixAutomatonOfBit sortTopologically()
{
int[] indeg = new int[gen];
for(int i = 0;i < gen;i++){
for(int j = 0;j < nodes[i].np;j++){
indeg[nodes[i].next[j].id]++;
}
}
Node[] sorted = new Node[gen];
sorted[0] = t0;
int p = 1;
for(int i = 0;i < gen;i++){
Node cur = sorted[i];
for(int j = 0;j < cur.np;j++){
if(--indeg[cur.next[j].id] == 0){
sorted[p++] = cur.next[j];
}
}
}
for(int i = 0;i < gen;i++)sorted[i].id = i;
nodes = sorted;
sortedTopologically = true;
return this;
}
// visualizer
public String toString()
{
StringBuilder sb = new StringBuilder();
for(Node n : nodes){
if(n != null){
sb.append(String.format("{id:%d, len:%d, link:%d, cloned:%b, ",
n.id,
n.len,
n.link != null ? n.link.id : null,
n.original.id));
sb.append("next:{");
for(int i = 0;i < n.np;i++){
sb.append(n.next[i].key + ":" + n.next[i].id + ",");
}
sb.append("}");
sb.append("}");
sb.append("\n");
}
}
return sb.toString();
}
public String toGraphviz(boolean next, boolean suffixLink)
{
StringBuilder sb = new StringBuilder("http://chart.apis.google.com/chart?cht=gv:dot&chl=");
sb.append("digraph{");
for(Node n : nodes){
if(n != null){
if(suffixLink && n.link != null){
sb.append(n.id)
.append("->")
.append(n.link.id)
.append("[style=dashed],");
}
if(next && n.next != null){
for(int i = 0;i < n.np;i++){
sb.append(n.id)
.append("->")
.append(n.next[i].id)
.append("[label=")
.append(n.next[i].key)
.append("],");
}
}
}
}
sb.append("}");
return sb.toString();
}
public String label(Node n)
{
if(n.original != null){
return n.id + "C";
}else{
return n.id + "";
}
}
public String toDot(boolean next, boolean suffixLink)
{
StringBuilder sb = new StringBuilder("digraph{\n");
sb.append("graph[rankdir=LR];\n");
sb.append("node[shape=circle];\n");
for(Node n : nodes){
if(n != null){
if(suffixLink && n.link != null){
sb.append("\"" + label(n) + "\"")
.append("->")
.append("\"" + label(n.link) + "\"")
.append("[style=dashed];\n");
}
if(next && n.next != null){
for(int i = 0;i < n.np;i++){
sb.append("\"" + label(n) + "\"")
.append("->")
.append("\"" + label(n.next[i]) + "\"")
.append("[label=\"")
.append(n.next[i].key)
.append("\"];\n");
}
}
}
}
sb.append("}\n");
return sb.toString();
}
}
void run() throws Exception
{
is = INPUT.isEmpty() ? System.in : new ByteArrayInputStream(INPUT.getBytes());
out = new PrintWriter(System.out);
long s = System.currentTimeMillis();
solve();
out.flush();
if(!INPUT.isEmpty())tr(System.currentTimeMillis()-s+"ms");
}
public static void main(String[] args) throws Exception {
new How_Many_Substrings().run();
}
private byte[] inbuf = new byte[1024];
public int lenbuf = 0, ptrbuf = 0;
private int readByte()
{
if(lenbuf == -1)throw new InputMismatchException();
if(ptrbuf >= lenbuf){
ptrbuf = 0;
try { lenbuf = is.read(inbuf); } catch (IOException e) { throw new InputMismatchException(); }
if(lenbuf <= 0)return -1;
}
return inbuf[ptrbuf++];
}
private boolean isSpaceChar(int c) { return !(c >= 33 && c <= 126); }
private int skip() { int b; while((b = readByte()) != -1 && isSpaceChar(b)); return b; }
private double nd() { return Double.parseDouble(ns()); }
private char nc() { return (char)skip(); }
private String ns()
{
int b = skip();
StringBuilder sb = new StringBuilder();
while(!(isSpaceChar(b))){ // when nextLine, (isSpaceChar(b) && b != ' ')
sb.appendCodePoint(b);
b = readByte();
}
return sb.toString();
}
private char[] ns(int n)
{
char[] buf = new char[n];
int b = skip(), p = 0;
while(p < n && !(isSpaceChar(b))){
buf[p++] = (char)b;
b = readByte();
}
return n == p ? buf : Arrays.copyOf(buf, p);
}
private char[][] nm(int n, int m)
{
char[][] map = new char[n][];
for(int i = 0;i < n;i++)map[i] = ns(m);
return map;
}
private int[] na(int n)
{
int[] a = new int[n];
for(int i = 0;i < n;i++)a[i] = ni();
return a;
}
private int ni()
{
int num = 0, b;
boolean minus = false;
while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
if(b == '-'){
minus = true;
b = readByte();
}
while(true){
if(b >= '0' && b <= '9'){
num = num * 10 + (b - '0');
}else{
return minus ? -num : num;
}
b = readByte();
}
}
private long nl()
{
long num = 0;
int b;
boolean minus = false;
while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
if(b == '-'){
minus = true;
b = readByte();
}
while(true){
if(b >= '0' && b <= '9'){
num = num * 10 + (b - '0');
}else{
return minus ? -num : num;
}
b = readByte();
}
}
private static void tr(Object... o) { System.out.println(Arrays.deepToString(o)); }
}