HDU 5008 Boring String Problem

题意:给定一个串长度<=1e5,将其所有的不同的字串按照字典序排序,然后q个询问,每次询问字典序第k小的的起始坐标,并且起始坐标尽量小。

分析:

一开始看错题意,没有意识到是求不同的字串中第k小的,果断不知道怎么做,感觉如果题目改成这样,似乎还有点难度,至少对我来说。

好了,这个题目是考虑不同的字串,首先后缀数组处理,也就是讲后缀按照字典序排序,对于每个后缀开始的字串,如h[i],容易知道i和i-1的后缀的LCP长度为h[i]那么i中除开前h[i]个字串,之后的字串在i-1之前都是没有出现过的,而且字典序要比之前的大,这样的字串数目时len-sa[i]-h[i],那么对于每个i用val[i]表示,sum[i]表示前i项的val值之和,对于第k小的字串,找到一个sum[i]>k,然后判断一下sum[i-1]是不是==k,不是的话说明第k小的字串一定在后缀i的字串中出现过,并算出长度L。然后再确定其在整个字符串中出现的最左位置,L>h[i]显然成立,所以L只能在i之后的后缀的字串中出现,找到一个范围i~r,使得之间的h值>=L,然后RMQ求出最小的sa值,也就是字串出现的最左位置。

话说暴力查找最左位置,竟然也能水过,而且速度还有快,测试数据里面肯定没有10000个a,每次询问第1小的字串这组数据。

考虑相同的字串,询问第k小的怎么做?

代码:

  1 #include <cstdio>
  2 #include <iostream>
  3 #include <vector>
  4 #include <algorithm>
  5 #define inf 0x0f0f0f0f
  6 #define pb push_back
  7 #define bug(x) printf("line %d: >>>>>>>>>>>>>>>\n", (x));
  8 #define in freopen("F:\\code\\data\\data.txt", "r", stdin);
  9 #define out freopen("F:\\code\\data\\data_out.txt", "w", stdout);
 10
 11 #define SZ(x) ((int)x.size())
 12 #define lson rt<<1, l, m
 13 #define rson rt<<1|1, m+1, r
 14 #define fi first
 15 #define se second
 16 using namespace std;
 17 typedef long long LL;
 18 const int maxn = (int)1e5 + 100;
 19 int s[maxn], sa[maxn], t[maxn], t2[maxn], rk[maxn], h[maxn], c[maxn];
 20 int n, m;
 21 LL lans, rans;
 22 LL sum[maxn], val[maxn];
 23
 24 void buildSa(int m)
 25 {
 26     int *x = t, *y = t2;
 27     for(int i = 0; i < m; i++) c[i] = 0;
 28     for(int i = 0; i < n; i++) c[x[i]=s[i]]++;
 29
 30     for(int i = 1; i < m; i++) c[i] += c[i-1];
 31     for(int i = n-1; i >= 0; i--) sa[--c[x[i]]] = i;
 32
 33     for(int k = 1; k <= n; k <<= 1)
 34     {
 35         int p = 0;
 36         for(int i = n-k; i < n; i++) y[p++] = i;
 37         for(int i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i]-k;
 38
 39         for(int i = 0; i < m; i++) c[i] = 0;
 40         for(int i = 0; i < n; i++) c[x[i]]++;//
 41         for(int i = 1; i < m; i++) c[i] += c[i-1];
 42         for(int i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
 43
 44         p = 1;
 45         swap(x, y);
 46         x[sa[0]] = 0;
 47
 48         for(int i = 1; i < n; i++)
 49             x[sa[i]] = y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k] ? p-1 : p++;
 50         m = p;
 51         if(p >= n)
 52             break;
 53     }
 54 }
 55 void getHeight()
 56 {
 57     int k = 0, j;
 58     h[0] = 0;
 59     for(int i = 0; i < n; i++) rk[sa[i]] = i;
 60     for(int i = 0; i < n; i++)
 61     {
 62         if(k) k--;
 63         if(rk[i] == 0)
 64             continue;
 65         j = sa[rk[i]-1];
 66         while(s[i+k] == s[j+k])
 67             k++;
 68         h[rk[i]] = k;
 69     }
 70 }
 71 int dp[maxn][20], mx[maxn][20];
 72 void rmqInit(int a[][20], int h[])
 73 {
 74     for(int i = 0; i < n; i++) a[i][0] = i;
 75     for(int k = 1; (1<<k) <= n; k++)
 76         for(int i = 0; i + (1<<k) <= n; i++)
 77             a[i][k] = h[a[i][k-1]] < h[a[i+(1<<(k-1))][k-1]] ? a[i][k-1] : a[i+(1<<(k-1))][k-1];
 78 }
 79 int RMQ(int l, int r, int a[][20], int h[])
 80 {
 81     if(l > r) swap(l, r);
 82     int k = 0;
 83     while((1<<(k+1)) < r-l+1) k++;
 84     return h[a[l][k]] <  h[a[r-(1<<k)+1][k]] ? a[l][k] : a[r-(1<<k)+1][k];
 85 }
 86 char str[maxn];
 87 int check(int l, int r)
 88 {
 89     return h[RMQ(l, r, dp, h)];
 90 }
 91 void solve(LL k)
 92 {
 93     if(k > sum[n-1])
 94     {
 95         lans = rans = 0;
 96         return;
 97     }
 98     int kk = upper_bound(sum+1, sum+n, k)-sum;
 99     if(sum[kk-1] == k)
100         kk--;
101     k -= sum[kk-1];
102
103     int len = h[kk]+k;
104     int x = sa[kk];
105     if(kk+1 < n && h[kk+1] >= len)
106     {
107         int l = kk+1, r = n;
108         while(r-l  > 1)
109         {
110             int mid = (l+r)>>1;
111             if(check(mid, kk+1) >= len)
112                 l = mid;
113             else r = mid;
114         }
115         x = min(x, sa[RMQ(kk+1, l, mx, sa)]);
116     }
117     lans = x+1, rans = x+len;
118 }
119 int main()
120 {
121
122
123     while(scanf("%s", str) == 1)
124     {
125         n = 0;
126         for(int i = 0; str[i]; i++)
127             s[n++] = str[i]-‘a‘+1;
128         s[n++] = 0;
129         buildSa(30);
130         getHeight();
131         rmqInit(dp, h);
132         rmqInit(mx, sa);
133 //        bug(1)
134         int q;
135         lans = rans = 0;
136         for(int i = 1; i < n; i++)
137         {
138             val[i] = n-1-sa[i]-h[i];
139             sum[i] = sum[i-1] + val[i];
140         }
141         for(int t = scanf("%d", &q); t <= q; t++)
142         {
143             LL v;
144             scanf("%I64d", &v);
145             LL k = (lans^rans^v)+1;
146             solve(k);
147             printf("%I64d %I64d\n", lans, rans);
148         }
149     }
150     return 0;
151 }

时间: 2024-10-24 22:43:14

HDU 5008 Boring String Problem的相关文章

HDU 5008 Boring String Problem(西安网络赛B题)

HDU 5008 Boring String Problem 题目链接 思路:构造后缀数组,利用height的数组能预处理出每个字典序开始的前缀和有多少个(其实就是为了去除重复串),然后每次二分查找相应位置,然后在往前往后找一下sa[i]最小的 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int

hdu 5008 Boring String Problem(后缀数组)

题目链接:hdu 5008 Boring String Problem 题目大意:给定一个字符串,初始状态l,r为0,每次询问子串中字典序第l^r^v+1的子串区间,对于重复的输出下标小的. 解题思路:后缀数组,对给定字符串做后缀数组,然后根据height数组确定每个位置做为起点的子串有多少,然后二分查找确定起点位置,但是因为子串的重复的要输出下表小的,所以确定起点后还要确定字典序最小的下标. #include <cstdio> #include <cstring> #includ

hdu 5008 Boring String Problem(后缀自动机构造后缀树)

hdu 5008 Boring String Problem(后缀自动机构造后缀树) 题意:给出一个字符串s,然后每次询问一个k,求s的所有子串中,字典序第k小的是谁?多个解,则输出最左边的那个 解题思路:这道题应该是为后缀树量身定制的吧.只要构造出了后缀树,然后按字典序遍历就可以得出每个节点包含的子串的字典序的范围了,而且必然是个连续的区间范围.但是我不会后缀树啊..比赛的时候突然想到,后缀自动机是可以构造后缀树的,虽然以前没写过,但还是硬着头皮上吧,居然还真的让我给撸出来了.我的做法是这样的

HDU - 5008 Boring String Problem (后缀数组+二分+RMQ)

Problem Description In this problem, you are given a string s and q queries. For each query, you should answer that when all distinct substrings of string s were sorted lexicographically, which one is the k-th smallest. A substring si...j of the stri

HDU 5008 Boring String Problem(后缀数组+二分)

题目链接 思路 想到了,但是木写对啊....代码 各种bug,写的乱死了.... 输出最靠前的,比较折腾... #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> #include <map> using namespace std; #define N 501000 #define LL __in

[后缀数组+二分+rmq] hdu 5008 Boring String Problem

有点小可惜这道题,当时整个思路都想到了,就是最后找最左下标的时候不会处理, 然后结束完发现直接暴力就可以了,想到了可是不敢写,10w个a直接就T了啊... 数据太弱了,敢写就过系列啊 T T. 然后希望有大神提供完美思路! 题意: 给一个字符串 然后n次询问 对于每一次询问给一个v 然后问第 l⊕r⊕v+1小的子串的区间   (⊕代表异或) 然后输出l r 这里的l r 就是上一次输出的l r  初始化是0 0 不存在输出0 0  如果多个 输出出现最早的. 思路: 首先后缀数组就不说了,做完之

HDOJ 5008 Boring String Problem

后缀数组+RMQ+二分 后缀数组二分确定第K不同子串的位置 , 二分LCP确定可选的区间范围 , RMQ求范围内最小的sa Boring String Problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 661    Accepted Submission(s): 183 Problem Description In thi

hdu 5008(2014 ACM/ICPC Asia Regional Xi&#39;an Online ) Boring String Problem(后缀数组&amp;二分)

Boring String Problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 219    Accepted Submission(s): 45 Problem Description In this problem, you are given a string s and q queries. For each que

HDU5008 Boring String Problem(后缀数组 + 二分 + 线段树)

题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5008 Description In this problem, you are given a string s and q queries. For each query, you should answer that when all distinct substrings of string s were sorted lexicographically, which one is