HDU6345-子串查询 线段树的区间查询
子串查询
Time Limit: 3500/3000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 276 Accepted Submission(s): 138
Problem Description
度度熊的字符串课堂开始了!要以像度度熊一样的天才为目标,努力奋斗哦!
为了检验你是否具备不听课的资质,度度熊准备了一个只包含大写英文字母的字符串 A[1,n]=a1a2⋯an,接下来他会向你提出 q 个问题 (l,r),你需要回答字符串 A[l,r]=alal+1⋯ar 内有多少个非空子串是 A[l,r] 的所有非空子串中字典序最小的。这里的非空子串是字符串中由至少一个位置连续的字符组成的子序列,两个子串是不同的当且仅当这两个子串内容不完全相同或者出现在不同的位置。
记 |S| 为字符串 S 的长度,对于两个字符串 S 和 T ,定义 S 的字典序比 T 小,当且仅当存在非负整数 k(≤min(|S|,|T|)) 使得 S 的前 k 个字符与 T 的前 k 个字符对应相同,并且要么满足 |S|=k 且 |T|>k,要么满足 k<min(|S|,|T|) 且 S 的第 k+1 个字符比 T 的第 k+1 个字符小。例如 "AA" 的字典序比 "AAA" 小,"AB" 的字典序比 "BA" 小。
为了检验你是否具备不听课的资质,度度熊准备了一个只包含大写英文字母的字符串 A[1,n]=a1a2⋯an,接下来他会向你提出 q 个问题 (l,r),你需要回答字符串 A[l,r]=alal+1⋯ar 内有多少个非空子串是 A[l,r] 的所有非空子串中字典序最小的。这里的非空子串是字符串中由至少一个位置连续的字符组成的子序列,两个子串是不同的当且仅当这两个子串内容不完全相同或者出现在不同的位置。
记 |S| 为字符串 S 的长度,对于两个字符串 S 和 T ,定义 S 的字典序比 T 小,当且仅当存在非负整数 k(≤min(|S|,|T|)) 使得 S 的前 k 个字符与 T 的前 k 个字符对应相同,并且要么满足 |S|=k 且 |T|>k,要么满足 k<min(|S|,|T|) 且 S 的第 k+1 个字符比 T 的第 k+1 个字符小。例如 "AA" 的字典序比 "AAA" 小,"AB" 的字典序比 "BA" 小。
Input
第一行包含一个整数 T,表示有 T 组测试数据。
接下来依次描述 T 组测试数据。对于每组测试数据:
第一行包含两个整数 n 和 q,表示字符串的长度以及询问的次数。
第二行包含一个长为 n 的只包含大写英文字母的字符串 A[1,n]。
接下来 q 行,每行包含两个整数 li,ri,表示第 i 次询问的参数。
保证 1≤T≤10,1≤n,q≤105,1≤li≤ri≤n。
接下来依次描述 T 组测试数据。对于每组测试数据:
第一行包含两个整数 n 和 q,表示字符串的长度以及询问的次数。
第二行包含一个长为 n 的只包含大写英文字母的字符串 A[1,n]。
接下来 q 行,每行包含两个整数 li,ri,表示第 i 次询问的参数。
保证 1≤T≤10,1≤n,q≤105,1≤li≤ri≤n。
Output
对于每组测试数据,先输出一行信息 "Case #x:"(不含引号),其中 x 表示这是第 x 组测试数据,接下来 q 行,每行包含一个整数,表示字符串 A[l,r] 中字典序最小的子串个数,行末不要有多余空格。
Sample Input
1
2 3
AB
1 1
1 2
2 2
Sample Output
Case #1:
1
1
1
思路:对输入的字符串建立线段树,父节点存子树的最小字母及其个数,若子树的最小字母相同,则父节点的个数为子树个数之和
代码:
#include <stdio.h> #include <iostream> #include <algorithm> #include <deque> #include <string.h> #define fio ios::sync_with_stdio(false);cin.tie(0);cout.tie(0) #define maxn 100009 #define inf 0x3f3f3f3f using namespace std; int t; int n, q; struct node_a { int v; int wz; //多余的 }a[maxn]; struct node_tree { int sum; //l-r区间中最小字母的个数 int l, r; int min; //最小字母,用1-26代替 int mid() { return (l + r) / 2; } }; node_tree tree[4 * maxn]; void buildtree(int l, int r, int root = 1) { tree[root].l = l; tree[root].r = r; if (l == r) { tree[root].sum = 1; tree[root].min = a[r].v; } else { int mid = (l + r) / 2; buildtree(l, mid, root * 2); buildtree(mid + 1, r, root * 2 + 1); if (tree[root * 2].min < tree[root * 2 + 1].min) { tree[root].min = tree[root * 2].min; tree[root].sum = tree[root * 2].sum; } else if (tree[root * 2].min > tree[root * 2 + 1].min) { tree[root].min = tree[root * 2 + 1].min; tree[root].sum = tree[root * 2 + 1].sum; } else { tree[root].min = tree[root * 2].min; tree[root].sum = tree[root * 2].sum + tree[root * 2 + 1].sum; }及其 } } node_tree getsum(int l, int r, int root = 1) { //区间查询,返回最小字母及其个数 if (tree[root].l == l && tree[root].r == r) { return tree[root]; } else { int mid = tree[root].mid(); int sum = 0; if (mid >= r) { return getsum(l, r, root * 2); } else if (mid < l) { return getsum(l, r, root * 2 + 1); } else { node_tree ltree = getsum(l, mid, root * 2); node_tree rtree = getsum(mid + 1, r, root * 2 + 1); ltree.l = l; ltree.r = r; if (ltree.min == rtree.min) { ltree.sum += rtree.sum; return ltree; } else if (ltree.min < rtree.min) { return ltree; } else { ltree = rtree; return ltree; } } } } int main() { fio; cin >> t; char c; int l, r; for (int i = 1; i <= t; i++) { memset(a, inf, sizeof a); memset(tree, inf, sizeof tree); cin >> n >> q; for (int i = 1; i <= n; i++) { cin >> c; a[i].v = c - 'A' + 1; a[i].wz = i; } buildtree(1, n); cout << "Case #" << i << ":" << endl; for (int i = 1; i <= q; i++) { cin >> l >> r; cout << getsum(l, r).sum << endl; } } return 0; }