马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
【题意】
给你一个字符串,让你从中提取处最多的连续子串,使得这些连续子串在次序保持稳定的环境下满足后一个是前一个的真子串(必须非空)。
【思绪】
这道题给我了一个重要的启发:就是一下子理不清晰思绪的时候可以打一个暴力,然后再思索怎么优化这个暴力。最开始我想直接想正解发现思绪非常的混乱,想了很久没有弄懂。但是打完了暴力以后就可以一目了然的看出要怎么优化了。
先把字符串反转,那么条件就变成了前一个是后一个的子串了。
观察一下发现几个性子:
1.后一个的长度是前一个的长度+1,即在后一个串的前面大概后面多加上一个字母,接下来我们逼迫这个条件满足
2. 如果某个串 S S S可以被提取出来(显然此前已经提取过了长度为1,2,…,|S|-1的串),那么它的任何一个子段都能被提取出来
然后我们写出一个时间空间都是 O ( n n ) O(n\sqrt n) O(nn )的暴力,由于最多有 n \sqrt n n 个段。
- #include <bits/stdc++.h>
- #define ll long long
- using namespace std;
- const int N = 5e5 + 10;
- int n, B; char s[N];
- int cnt = 1, last = 1, fa[N << 1], len[N << 1], ch[N << 1][26];
- int pos[N], st[N << 1], siz[N << 1];
- vector<int> dp[N << 1], e[N << 1];
- int add(int w) {
- int now = ++cnt, p = last; last = now;
- len[now] = len[p] + 1;
- for (; p && !ch[p][w]; p = fa[p]) {
- ch[p][w] = now;
- }
- if (!p) {
- fa[now] = 1;
- }
- else {
- int q = ch[p][w];
- if (len[q] == len[p] + 1) {
- fa[now] = q;
- }
- else {
- int nw = ++cnt;
- len[nw] = len[p] + 1, fa[nw] = fa[q];
- fa[q] = fa[now] = nw;
- memcpy(ch[nw], ch[q], sizeof(ch[q]));
- for (; p && ch[p][w] == q; p = fa[p]) {
- ch[p][w] = nw;
- }
- }
- }
- return now;
- }
- void dfs(int x) {
- if (len[fa[x]] + 1 <= B) {
- st[x] = x;
- }
- for (int y : e[x]) {
- st[y] = st[x];
- dfs(y);
- }
- }
- int main() {
- // freopen("in.in", "r", stdin);
- ios::sync_with_stdio(false);
- cin.tie(nullptr);
- cin >> n >> (s + 1);
- reverse(s + 1, s + n + 1);
- for (int i = 1; i <= n; i++) {
- int w = s[i] - 'a';
- pos[i] = add(w);
- }
- for (int i = 1; i <= n; i++) {
- if (i * (i + 1) / 2 > n) {
- break;
- }
- B = i;
- }
- for (int i = 2; i <= cnt; i++) {
- if (len[fa[i]] + 1 <= B) {
- int t = min(len[i], B) - len[fa[i]];
- dp[i].resize(t + 1);
- siz[i] = t;
- }
- e[fa[i]].push_back(i);
- }
- dfs(1);
- int ans = 0;
- for (int i = 1; i <= n; i++) {
- int p = st[pos[i]];
- int ret = 1;
- for (; p; p = fa[p]) {
- int now = len[fa[p]] + siz[p];
- bool flag = 0;
- for (int j = siz[p]; j >= 1; j--) {
- if (dp[p][j] && dp[p][j] + now < i) {
- ret = now + 1; flag = 1; break;
- }
- now--;
- }
- if (flag) {
- break;
- }
- }
- p = st[pos[i - 1]];
- for (; p; p = fa[p]) {
- int now = len[fa[p]] + siz[p];
- bool flag = 0;
- for (int j = siz[p]; j >= 1; j--) {
- if (dp[p][j] && dp[p][j] + now < i) {
- ret = max(ret, now + 1); flag = 1; break;
- }
- now--;
- }
- if (flag) {
- break;
- }
- }
- ans = max(ans, ret);
- p = st[pos[i]];
- for (; p; p = fa[p]) {
- for (int j = min(siz[p], ret - len[fa[p]]); j >= 1; j--) {
- if (!dp[p][j]) {
- dp[p][j] = i;
- }
- }
- }
- }
- cout << ans << endl;
- }
复制代码 细致观察了一下,发现许多可以优化的地方。
由于最后修改是把某个点往上的全部路径修改为i,因此满足单调性,而且修改的总数为 O ( n ) O(n) O(n)。
而且必要满足的 d p [ p ] [ j ] + n o w dp[p][j] + now dp[p][j]+now也有单调性,以是可以用倍增来找到点,点内开个vector存段,在点内探求的时候用二分,就能做到时间空间都是 O ( n l o g n ) O(n log n) O(nlogn)。
- #include <bits/stdc++.h>
- #define ll long long
- using namespace std;
- const int N = 5e5 + 10;
- int n; char s[N];
- int cnt = 1, last = 1, fa[N << 1], len[N << 1], ch[N << 1][26];
- int pos[N], f[N << 1][20];
- vector<pair<int, int>> dp[N << 1];
- int add(int w) {
- int now = ++cnt, p = last; last = now;
- len[now] = len[p] + 1;
- for (; p && !ch[p][w]; p = fa[p]) {
- ch[p][w] = now;
- }
- if (!p) {
- fa[now] = 1;
- }
- else {
- int q = ch[p][w];
- if (len[q] == len[p] + 1) {
- fa[now] = q;
- }
- else {
- int nw = ++cnt;
- len[nw] = len[p] + 1, fa[nw] = fa[q];
- fa[q] = fa[now] = nw;
- memcpy(ch[nw], ch[q], sizeof(ch[q]));
- for (; p && ch[p][w] == q; p = fa[p]) {
- ch[p][w] = nw;
- }
- }
- }
- return now;
- }
- int main() {
- // freopen("in.in", "r", stdin);
- ios::sync_with_stdio(false);
- cin.tie(nullptr);
- cin >> n >> (s + 1);
- reverse(s + 1, s + n + 1);
- for (int i = 1; i <= n; i++) {
- int w = s[i] - 'a';
- pos[i] = add(w);
- }
- for (int i = 2; i <= cnt; i++) {
- f[i][0] = fa[i];
- }
- for (int j = 1; j <= 19; j++) {
- for (int i = 1; i <= cnt; i++) {
- f[i][j] = f[f[i][j - 1]][j - 1];
- }
- }
- auto calc = [](int p, int pos) {
- int ret = 1;
- for (int j = 19; j >= 0; j--) {
- int q = f[p][j];
- if (q <= 1) continue;
- if (!dp[q].size() || len[fa[q]] + 1 + dp[q][0].second >= pos) {
- p = q;
- }
- }
- if (p && (!dp[p].size() || len[fa[p]] + 1 + dp[p][0].second >= pos)) p = fa[p];
- int l = 0, r = (int)dp[p].size() - 1, mid, pre = -1;
- while (l <= r) {
- mid = (l + r) / 2;
- int minlen = mid ? dp[p][mid - 1].first + 1 : len[fa[p]] + 1;
- if (minlen + dp[p][mid].second < pos) {
- pre = mid; l = mid + 1;
- }
- else {
- r = mid - 1;
- }
- }
- if (pre != -1) {
- ret = max(ret, min(pos - 1 - dp[p][pre].second, dp[p][pre].first) + 1);
- }
- return ret;
- };
- int ans = 1;
- for (int i = 1; i <= n; i++) {
- int ret = max(calc(pos[i], i), calc(pos[i - 1], i));
- ans = max(ans, ret);
- int p = pos[i];
- for (int j = 19; j >= 0; j--) {
- int q = f[p][j];
- if (q > 1 && len[fa[q]] + 1 > ret) {
- p = q;
- }
- }
- if (p > 1 && len[fa[p]] + 1 > ret) p = fa[p];
- while (p > 1) {
- pair<int, int> now = {min(ret, len[p]), i};
- int last = !dp[p].size() ? len[fa[p]] : dp[p][(int)dp[p].size() - 1].first;
- if (last < now.first) {
- dp[p].push_back(now);
- p = fa[p];
- }
- else {
- break;
- }
- }
- }
- cout << ans << endl;
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |