/**
* April 2013 Long Challenge at Codechef
*
* Problem: STRQUERY - String Query
* Author: Anton Lunyov (Editorialist)
* Complexity: O(Q * log^2 Q + (sum of |q|) * log Q)
* Timing: 0.80 out of 1.00
*
* Variable or method names in comments
* are sometimes surrounded by grave accent signs, like `q`.
*/
#include <vector>
#include <string>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
// Supports the following types of queries on the string S:
// - match(q) - returns the number of occurrences of the string `q` in S,
// complexity is O(|q| * log |S|).
// - pop() - deletes the first character of S and returns its value,
// complexity is O(log |S|).
// - push(c) - inserts character `c` at the beginning of the string,
// complexity is O(log^2 |S|).
// - size() - returns the length of the string,
// complexity is O(1).
// - at(i) - returns the i-th character of S in 0-based numeration,
// complexity is O(1).
// - revat(i) - returns the i-th character of S reversed (in 0-based numeration),
// it will be convenient for the main class StringDeque,
// complexity is O(1).
// Occupies O(|S|) memory at each moment.
class DynamicSuffixArray {
private:
// The data structure for node of the treap.
struct Node {
int left, right, parent;
int size; // The size of the subtree.
int y; // The key for heap ordering.
// Creates node having all zero links, but with size 1.
// We will call such node as default node.
// rand() for y keeps the tree self-balancing.
Node(): y(rand()), left(0), right(0), parent(0), size(1) {};
};
// Node nodes_[id] corresponds to the suffix that starts at str_[id].
vector<char> str_; // The actual string in reversed order.
vector<Node> nodes_; // The nodes of the treap.
int root_; // nodes_[root_] is the root of the treap.
public:
// Creates dummy zero character and zero node, and sets root to 0.
DynamicSuffixArray() {
str_.push_back(0);
nodes_.push_back(Node());
nodes_[0].size = 0; // The size is 1 by default so we fix this.
root_ = 0; // Indicates that the tree is empty.
}
// To construct dynamic suffix array by string we should push all characters
// of the string in reversed order. So complexity is O(|S| * log^2 |S|).
// TODO: Implement a constructor by string using usual suffix array
// with complexity O(|S| * log |S|) or even O(|S|).
// Returns the number of occurrences of the string `q` in the string S.
int match(const string& q) {
return match(root_, q, false, false);
}
// Deletes the first character of the string and returns its value.
char pop() {
remove(size()); // Removes last node from the tree and updates the root.
nodes_.pop_back();
char c = str_.back();
str_.pop_back();
return c;
}
// Inserts character `c` to the beginning of the string.
void push(char c) {
str_.push_back(c); // We add `c` to the array `str_`.
nodes_.push_back(Node()); // We add default node to the array `nodes_`...
// ...and then actually insert this node to the tree and update the root.
root_ = insert(root_, size());
}
// Returns the length of the string.
int size() {
return str_.size() - 1; // "-1" because of the dummy zero character.
}
// Returns the i-th character of the string in 0-based numeration.
char at(int i) {
// Recall that the 0-th character of S is `size()`-th character in `str_`.
return str_[size() - i];
}
// Returns the i-th character of the reversed string in 0-based numeration.
char revat(int i) {
return str_[i + 1]; // "+1" because of the dummy zero character.
}
private:
// See editorial for details.
int match(int tree, const string& q, bool matchLeft, bool matchRight) {
// For empty subtree the answer is 0.
if (!tree) {
return 0;
}
// In this case all suffixes in subtree start with `q`.
if (matchLeft && matchRight) {
// So the answer is the size of this subtree.
return nodes_[tree].size;
}
for (int i = 0; i < q.size(); ++i) {
if (q[i] > str_[tree - i]) {
// Here `matchLeft` should be set to false.
return match(nodes_[tree].right, q, false, matchRight);
} else if (q[i] < str_[tree - i]) {
// While here `matchRight` should be set to false.
return match(nodes_[tree].left, q, matchLeft, false);
}
}
return 1 +
match(nodes_[tree].left, q, matchLeft, true) +
match(nodes_[tree].right, q, true, matchRight);
}
// Sets parent of node `id` to `parent_id` if `id` is non-zero.
void setParent(int id, int parent_id) {
if (id) {
nodes_[id].parent = parent_id;
}
}
// Sets field `size` of the node `id` to the actual size of its subtree.
// Modifies only non-zero nodes.
void fixSize(int id) {
if (id) {
nodes_[id].size = 1 +
nodes_[nodes_[id].left].size +
nodes_[nodes_[id].right].size;
}
}
// Removes node `id` from the tree and update the root.
// Vector `nodes_` does not change.
void remove(int id) {
int par = nodes_[id].parent;
int tmp = merge(nodes_[id].left, nodes_[id].right);
// `tmp` is id of node that contains merge of children of the node `id`.
// `par` is the parent of `tmp` since `tmp` is replacement of `id`.
setParent(tmp, par);
if (par) {
// If `id` has parent then root remains the same,
// but we need to replace `id` by `tmp` in sons of the node `par`.
if(nodes_[par].left == id) {
nodes_[par].left = tmp;
} else {
nodes_[par].right = tmp;
}
while (par) {
// Since we delete one node in subtree of `id`,
// we should decrease the field `size` of each its ancestor by 1.
nodes_[par].size--;
par = nodes_[par].parent;
}
} else {
// Otherwise `id` was the root and the new root is `tmp`.
root_ = tmp;
}
}
// Merges subtrees rooted at `L` and `R` and save result in one of the them.
// Each node in the subtree rooted at `L` should be less than
// each node in the subtree rooted at `R`.
int merge(int L, int R) {
// We return L when R is 0 and R when L is 0.
if (!L || !R) {
return L + R;
}
// We should preserve the heap property by field `y`.
if (nodes_[L].y < nodes_[R].y) {
// In this case `R` should be a root and we merge `L` and left son of `R`,
// and result is saved in `tmp`.
int tmp = merge(L, nodes_[R].left);
// `tmp` will be the new left son of `R`.
setParent(tmp, R);
nodes_[R].left = tmp;
fixSize(R);
return R;
} else {
// In this case `L` will be a root.
int tmp = merge(nodes_[L].right, R);
setParent(tmp, L);
nodes_[L].right = tmp;
fixSize(L);
return L;
}
}
// Returns the index of `id`-th suffix (the suffix starting at position `id`)
// in the sorted array of all suffixes (empty suffix has index 0),
// the so-called order statistic of the suffix.
int getIndex(int id) {
// Zero node corresponds to empty suffix and has zero index.
if (!id) {
return 0;
}
// We add one more the size of its left subtree to the answer.
int index = nodes_[nodes_[id].left].size + 1;
while (nodes_[id].parent) {
int par = nodes_[id].parent; // Ancestor of the initial node `id`
if (id == nodes_[par].right) {
// If node `id` is the right son of `par`,
// we add one more the size of left subtree of `par` to the answer as above.
index += nodes_[nodes_[par].left].size + 1;
}
id = par;
}
return index;
}
// Returns result of comparison of i-th and j-th suffixes.
// Result is negative if i-th suffix smaller, positive if it's larger,
// and zero when they are equal (i.e. when i = j).
// It is not assumed these suffixes has correct positions in the tree,
// but (i-1)-th and (j-1)-th suffixes should have ones.
int compare(int i, int j) {
// At first we compare their first characters.
int cmp = str_[i] - str_[j];
if (cmp == 0)
// If they are different we compare order statistics
// for (i-1)-th and (j-1)-th suffixes.
cmp = getIndex(i - 1) - getIndex(j - 1);
return cmp;
}
// Inserts node `id` to the subtree rooted at `tree`
// and returns the new root of this subtree (it may change).
int insert(int tree, int id) {
// In the case of empty subtree the node `id` will be a new root.
if (!tree) {
return id;
}
// The treap should be a heap by `y` fields.
// Hence if `id` has larger `y` than `tree`, `id` should be a new root.
if (nodes_[tree].y < nodes_[id].y) {
// In this case we split subtree rooted at `tree` into two parts:
// 1) with suffixes < the `id`-th suffix (the root is left son of `id`);
// 2) with suffixes > the `id`-th suffix (the root is right son of `id`).
split(tree, id, nodes_[id].left, nodes_[id].right);
// We set parents of both left and right sons if `id` to be `id`.
setParent(nodes_[id].left, id);
setParent(nodes_[id].right, id);
fixSize(id);
return id; // This case is terminal for insert query.
}
// Otherwise we should move `id` to one of the subtrees of the node `tree`,
// selecting the correct subtree by comparing suffixes at `tree` and `id`.
int cmp = compare(id, tree);
if (cmp < 0) {
// If `id` is less than `tree` than we insert it to the left subtree.
// `tmp` is the root of this subtree after inserting.
int tmp = insert(nodes_[tree].left, id);
// And since root of this subtree could change,
// we fix relation between `tree` and `tmp`
setParent(tmp, tree);
nodes_[tree].left = tmp;
} else {
// Now cmp > 0 since all suffixes are different
// and we insert `id` to the right subtree.
int tmp = insert(nodes_[tree].right, id);
setParent(tmp, tree);
nodes_[tree].right = tmp;
}
// We should fix the field `size` of the node `tree`.
// We can simply increase it by 1 since we add only one node to its subtree.
nodes_[tree].size++;
return tree; // `tree` remains the root of its subtree
}
// Splits the subtree rooted at `tree` by the node `id` into two parts:
// 1) with suffixes < the `id`-th suffix (the root is `L`);
// 2) with suffixes > the `id`-th suffix (the root is `R`).
// `id`-th node does not have to be a correct node of the tree.
// But node `id - 1` should be.
void split(int tree, int id, int& L, int& R) {
// In the case of empty subtree there is nothing to split,
// and both `L` and `R` should be empty trees as well.
if (!tree) {
L = R = 0;
return;
}
// Otherwise we compare the root of the subtree with the node `id`.
int cmp = compare(id, tree);
if (cmp < 0) {
// If `id` < `tree` then we should split left subtree of `tree` by `id`
// and place the second subtree to the root of left subtree itself,
split(nodes_[tree].left, id, L, nodes_[tree].left);
setParent(nodes_[tree].left, tree);
R = tree;
} else {
// Otherwise cmp > 0 and we split right subtree by the node `id`.
split(nodes_[tree].right, id, nodes_[tree].right, R);
setParent(nodes_[tree].right, tree);
L = tree;
}
fixSize(tree);
}
};
// Returns the prefix function of the string s. Namely, pf[i]
// is the length of longest proper prefix of s that is the suffix of s[0..i].
// Classical Knuth-Morris-Pratt preprocessing with complexity O(|s|) is used.
vector<int> prefixFunction(const string& s) {
int n = s.size();
vector<int> pf(n);
pf[0] = 0;
for (int i = 1; i < n; ++i) {
pf[i] = pf[i - 1];
while (pf[i] && s[i] != s[pf[i]]) {
pf[i] = pf[pf[i] - 1];
}
if (s[i] == s[pf[i]]) {
pf[i]++;
}
}
return pf;
}
// Returns the number of occurrences of the string `q` in the string `s`.
// `pf` should contain the computed prefix function of `q`.
// Classical Knuth-Morris-Pratt algorithm with complexity O(|s|) is used.
int match(const string& q, const vector<int>& pf, const string &s)
{
int n = s.size();
int count = 0;
int pr = 0;
for (int i = 0; i < n; ++i) {
char c = s[i];
while (pr > 0 && q[pr] != c) {
pr = pf[pr - 1];
}
if (q[pr] == c) pr++;
if (pr == q.size()) {
count++;
pr = pf[pr - 1];
}
}
return count;
}
// Returns the reverse of the string `s`
string reverse(string s) {
reverse(s.begin(), s.end());
return s;
}
// Supports the following types of queries on the string S:
// - pushRight(c) - inserts character `c` to the end of the string.
// - pushLeft(c) - inserts character `c` to the beginning of the string.
// - popRight() - deletes the last character of S and returns its value.
// - popLeft() - deletes the first character of S and returns its value.
// - size() - returns the length of the string.
// - at(i) - returns the i-th character of S in 0-based numeration.
// - match(q) - returns the number of occurrences of the string `q` in S.
// Occupies O(|S|) memory at each moment.
class StringDeque {
private:
// At each moment `left_` contains some prefix A of S,
// while `right_` contains the remaining suffix B of S but in reversed order.
// Thus, S = A + B = left_ + reverse(right_)
DynamicSuffixArray left_;
DynamicSuffixArray right_;
public:
StringDeque(const string& s) {
// For ease of code we simply push all characters to the `right_`,
// keeping `left_` empty.
for (int i = 0; i < s.size(); ++i) {
pushRight(s[i]);
}
}
void pushRight(char c) {
right_.push(c);
}
void pushLeft(char c) {
left_.push(c);
}
char popRight() {
if (!right_.size()) {
// In this case we should pop the last character of `left_`, which
// is not supported. Hence in O(|S|) time we divide `left_` into
// nearly equal parts and assign reverse of the second part to `right_`.
equalize(right_, left_);
}
return right_.pop();
}
char popLeft() {
if (!left_.size()) {
// Now `right_` contains S reversed. So we divide it into nearly
// equal parts, and assign the reverse of the second part to `left_`,
// which is exactly the prefix of S since `right_` contains S reversed.
equalize(left_, right_);
}
return left_.pop();
}
int size() {
return left_.size() + right_.size();
}
char at(int i) {
if (i < left_.size()) {
return left_.at(i);
}
// revat() is called since `right_` contains the reversed suffix of S.
return right_.revat(i - left_.size());
}
// `pf` should be correctly computed prefix function of `q`
int match(const string& q, const vector<int>& pf) {
// `border` is a concatenation of last min(|A|, |q|-1) characters of A
// and first min(|B|, |q|-1) characters of B,
// where A and B are defined above.
string border = "";
for (int i = max(0, left_.size() - (int)q.size() + 1); i < left_.size(); ++i) {
border += left_.at(i);
}
for (int i = 0; i < (int)q.size() - 1 && i < right_.size(); ++i) {
// To access i-th character of B we should access i-th character
// of `right_` in reversed order, hence `revat()`.
border += right_.revat(i);
}
// Each occurrence of `q` in S is either in A, or in B or in `border`.
// And since `border` contains less than |q| characters from A and from B,
// this occurrences are different. Since `right_` contains reversed
// suffix of S we pass reversed `q` to right_.match().
return left_.match(q) + right_.match(reverse(q)) + ::match(q, pf, border);
}
private:
// `a` contains empty string, `b` is not.
// `a` will contain last (|b|+1) div 2 characters of `b` in reversed order,
// while `b` will contain the last |b| div 2 its characters.
// So `a` will be non-empty after that.
void equalize(DynamicSuffixArray& a, DynamicSuffixArray& b) {
int n = b.size();
int m = n / 2;
for (int i = m; i < n; ++i) {
a.push(b.at(i));
}
// `tmp` will contain first `m` characters of `b` in reversed order.
string tmp = "";
for (int i = m - 1; i >= 0; --i) {
tmp += b.at(i);
}
// We clear `b` and push characters of `tmp` to `b`.
b = DynamicSuffixArray();
for (int i = 0; i < m; ++i) {
b.push(tmp[i]);
}
}
};
char buf[1500001]; // Needed to read strings fast using scanf.
// Reads the string `s` from stdin.
void read(string& s) {
scanf("%s", buf);
s = buf;
}
int main() {
string str0; // Initial string.
read(str0);
int L = str0.size();
// `left` will always contain first (|S| div 2) characters of S,
// while `right` will contain the remaining characters of S.
// This is equivalent to |left| <= |right| <= |left| + 1.
StringDeque left(str0.substr(0,L/2));
StringDeque right(str0.substr(L/2));
int Q;
scanf("%d", &Q);
for (int it = 0; it < Q; ++it) {
string op; // The current operation.
read(op);
// In the case of insert operation we read the character we need to insert.
char c;
if(op.substr(0,6) == "INSERT") {
scanf(" %c", &c);
}
if (op == "INSERT_LEFT") {
left.pushLeft(c);
} else if (op == "INSERT_RIGHT") {
right.pushRight(c);
} else if (op == "INSERT_MIDDLE") {
if(left.size() == right.size()) {
right.pushLeft(c);
} else {
left.pushRight(c);
}
} else if (op == "DELETE_LEFT") {
left.popLeft();
} else if (op == "DELETE_RIGHT") {
right.popRight();
} else if (op == "DELETE_MIDDLE") {
right.popLeft();
} else { // op == "QUERY"
string q;
read(q);
vector<int> pf = prefixFunction(q);
int L = q.size();
int L1 = left.size();
int L2 = right.size();
// `border` is a concatenation of suffix of `left` of length min(L1, L-1)
// and prefix of `right` of length min(L2, L-1)
string border = "";
for (int i = max(0, L1 - L + 1); i < L1; ++i) {
border += left.at(i);
}
for (int i = 0; i < L2 && i < L - 1; ++i) {
border += right.at(i);
}
// The number of occurrences of `q` is counted similarly as in StringDeque.
int count = left.match(q, pf) + right.match(q, pf) + match(q, pf, border);
printf("%d\n", count);
}
// After insert or delete queries condition |left| <= |right| <= |left| + 1
// could be broken, so we equalize `left` and `right` by additional
// movements of characters from one of them to another.
while (left.size() + 2 <= right.size()) {
left.pushRight(right.popLeft());
}
while (left.size() > right.size()) {
right.pushLeft(left.popRight());
}
}
return 0;
}