CF 1320D Reachable Strings 后缀数组

确定性解法:后缀数组

众所周知 hash 由于不能完全避免冲突常常会被 cf hacker 卡掉,此题有更稳定的解法

考虑将原串进行简化,由于只有连续的 1 会造成不确定的匹配,可以只对 0 建立一个新串

对于一段连续的 1,后面的 0 建立新字符,如果有奇数个 1 就建立 1,偶数个就建立 0

例如: 1110 -> 1,110 -> 0,11011 -> 0

由于最后的 1 是没有后导 0 的就不计入新串

然后新串的 lcp 就可以对应到原串的对应位置,新串 lcp 匹配就等价于原串可达

比如 011010 和 110010 转换后都会变为 001

然后考虑左右两端不是 0 的情况

就类似分块,将左右边界用 1 的数量特判即可,特判的情况很多注意细节

  • 区间全是 1
  • 区间只有一个 0 (边界特判完后没有 0)
#include <bits/stdc++.h>

#define ll long long
#define X first
#define Y second
#define sz size()
#define all(x) x.begin(), x.end()
using namespace std;

typedef pair<int, int> pii;
typedef vector<int> vi;
typedef vector<long long> vl;

template<class T>
inline bool scan(T &ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; //EOF
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}

const ll mod = 1e9 + 7;
const int maxn = 2e5 + 50;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;

ll qp(ll x, ll n) {
    ll res = 1;
    x %= mod;
    while (n > 0) {
        if (n & 1) res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

char t[maxn], s[maxn];

namespace SA {
    const int maxn = 2e5 + 10;
    int t1[maxn], t2[maxn], c[maxn];
    int Rank[maxn], height[maxn];
    int RMQ[maxn];
    int mm[maxn];
    int sa[maxn];
    int best[25][maxn];
    bool cmp(int *r, int a, int b, int l) {
        return r[a] == r[b] && r[a + l] == r[b + l];
    }
    void da(char str[], int sa[], int Rank[], int height[], int n, int m) {
        n++;
        int i, j, p, *x = t1, *y = t2;
        for (i = 0; i < m; i++)c[i] = 0;
        for (i = 0; i < n; i++)c[x[i] = str[i]]++;
        for (i = 1; i < m; i++)c[i] += c[i - 1];
        for (i = n - 1; i >= 0; i--)sa[--c[x[i]]] = i;
        for (j = 1; j <= n; j <<= 1) {
            p = 0;
            for (i = n - j; i < n; i++)y[p++] = i;
            for (i = 0; i < n; i++)if (sa[i] >= j)y[p++] = sa[i] - j;
            for (i = 0; i < m; i++)c[i] = 0;
            for (i = 0; i < n; i++)c[x[y[i]]]++;
            for (i = 1; i < m; i++)c[i] += c[i - 1];
            for (i = n - 1; i >= 0; i--)sa[--c[x[y[i]]]] = y[i];
            swap(x, y);
            p = 1;
            x[sa[0]] = 0;
            for (i = 1; i < n; i++)
                x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;
            if (p >= n)break;
            m = p;
        }
        int k = 0;
        n--;
        for (i = 0; i <= n; i++)Rank[sa[i]] = i;
        for (i = 0; i < n; i++) {
            if (k)k--;
            j = sa[Rank[i] - 1];
            while (str[i + k] == str[j + k])k++;
            height[Rank[i]] = k;
        }
    }
    void initRMQ(int n) {
        for (int i = 1; i <= n; i++)
            RMQ[i] = height[i];
        mm[0] = -1;
        for (int i = 1; i <= n; i++)
            mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
        for (int i = 1; i <= n; i++)best[0][i] = i;
        for (int i = 1; i <= mm[n]; i++)
            for (int j = 1; j + (1 << i) - 1 <= n; j++) {
                int a = best[i - 1][j];
                int b = best[i - 1][j + (1 << (i - 1))];
                if (RMQ[a] < RMQ[b])best[i][j] = a;
                else best[i][j] = b;
            }
    }
    int askRMQ(int a, int b) {
        int t;
        t = mm[b - a + 1];
        b -= (1 << t) - 1;
        a = best[t][a];
        b = best[t][b];
        return RMQ[a] < RMQ[b] ? a : b;
    }
    int lcp(int a, int b) {
        a = Rank[a];
        b = Rank[b];
        if (a > b) swap(a, b);
        //cout << askRMQ(a + 1, b) << endl;
        return height[askRMQ(a + 1, b)];
    }
    void preprocess(char *str, int n, int m) {
        da(str, sa, Rank, height, n, m);
        initRMQ(n);
    }
}

int n, q;
int sum[maxn], L[maxn], R[maxn], id[maxn];

int main(int argc, char *argv[]) {
    scanf("%d%s%d", &n, t + 1, &q);
    int pre = -1;
    for (int i = 1; i <= n; ++i) {
        sum[i] = sum[i - 1] + (t[i] == '0');
        if (t[i] == '0') pre = i;
        L[i] = pre;
    }
    pre = -1;
    for (int i = n; i >= 1; --i) {
        if (t[i] == '0') pre = i;
        R[i] = pre;
    }
    int tot = 0;
    pre = 0;
    for (int i = 1; i <= n; ++i) {
        if (t[i] == '0') {
            s[id[i] = tot++] = (pre & 1) + '0';
            pre = 0;
        } else pre++;
    }
    SA::preprocess(s, tot, 130);
    for (int i = 1; i <= q; ++i) {
        int l1, l2, len;
        scanf("%d%d%d", &l1, &l2, &len);
        int r1 = l1 + len - 1;
        int r2 = l2 + len - 1;
        /*putchar(' ');
        for (int j = l1; j <= r1; ++j) putchar(s[j]);
        putchar(' ');
        for (int j = l2; j <= r2; ++j) putchar(s[j]);
        putchar(' ');*/
        int z1 = sum[r1] - sum[l1 - 1];
        int z2 = sum[r2] - sum[l2 - 1];
        if (z1 != z2) {
            puts("NO");
            continue;
        }
        if (z1 == 0) {
            puts("YES");
            continue;
        }
        int front1 = R[l1] - l1, front2 = R[l2] - l2, last1 = r1 - L[r1], last2 = r2 - L[r2];
        if (front1 % 2 != front2 % 2 || last1 % 2 != last2 % 2) {
            puts("NO");
            continue;
        }
        if (z1 == 1) {
            puts("YES");
            continue;
        }
        if (id[R[l1]] == id[R[l2]]) {
            puts("YES");
            continue;
        }
        int alen = SA::lcp(id[R[l1]] + 1, id[R[l2]] + 1);
        if (alen >= z1 - 1) {
            puts("YES");
        } else puts("NO");
    }
    return 0;
}

原文地址:https://www.cnblogs.com/badcw/p/12428851.html

时间: 2024-08-10 16:22:42

CF 1320D Reachable Strings 后缀数组的相关文章

poj 2406 Power Strings 后缀数组解法

连续重复子串问题 poj 2406 Power Strings http://poj.org/problem?id=2406 问一个串能否写成a^n次方这种形式. 虽然这题用kmp做比较合适,但是我们还是用后缀数组做一做,巩固后缀数组的能力. 对于一个串,如果能写出a^n这种形式,我们可以暴力枚举循环节长度L,那么后缀suffix(1)和suffix(1 + L)的LCP应该就是 lenstr - L.如果能满足,那就是,不能,就不是. 这题的话da算法还是超时,等我学了DC3再写上来. 其实这

POJ2406:Power Strings(后缀数组DC3)

Description Given two strings a and b we define a*b to be their concatenation. For example, if a = "abc" and b = "def" then a*b = "abcdef". If we think of concatenation as multiplication, exponentiation by a non-negative inte

CF(427D-Match &amp;amp; Catch)后缀数组应用

题意:给两个字符串,求一个最短的子串.使得这个子串在两个字符串中出现的次数都等于1.出现的定义为:能够重叠的出现. 解法:后缀数组的应用.从小枚举长度.假设一个长度len合法的话:则一定存在这个样的sa[i]排名.sa[i]与s[i+1]的公共前缀长度大于等于len,且sa[i]与[i-1]的公共前缀长度小于len,同一时候sa[i+1]与[i+2]的公共前缀长度小于len,同一时候保证sa[i]与sa[i+1]在两个串中.Judge函数就是技巧性地实现了这些推断. 代码: #include<i

CF(427D-Match &amp; Catch)后缀数组应用

题意:给两个字符串,求一个最短的子串.使得这个子串在两个字符串中出现的次数都等于1.出现的定义为:可以重叠的出现. 解法:后缀数组的应用.从小枚举长度.如果一个长度len合法的话:则一定存在这个样的sa[i]排名.sa[i]与s[i+1]的公共前缀长度大于等于len,且sa[i]与[i-1]的公共前缀长度小于len,同时sa[i+1]与[i+2]的公共前缀长度小于len,同时保证sa[i]与sa[i+1]在两个串中.Judge函数就是技巧性地实现了这些判断. 代码: #include<iostr

ZOJ1905Power Strings (KMP||后缀数组+RMQ求循环节)

Given two strings a and b we define a*b to be their concatenation. For example, if a = "abc" and b = "def" then a*b = "abcdef". If we think of concatenation as multiplication, exponentiation by a non-negative integer is defin

Power Strings POJ - 2406 后缀数组

Given two strings a and b we define a*b to be their concatenation. For example, if a = "abc" and b = "def" then a*b = "abcdef". If we think of concatenation as multiplication, exponentiation by a non-negative integer is defin

CF 427D 后缀数组

大意是寻找两个字符串中最短的公共子串,要求子串在两个串中都是唯一的. 造一个S#T的串,做后缀数组,从小到大枚举子串长度在height数组中扫描,如果某一个组中来自两个串的数量分别为1,就找到了答案. 1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <string> 5 #include <string.h> 6 #include <st

POJ2406 Power Strings(KMP,后缀数组)

这题可以用后缀数组,KMP方法做 后缀数组做法开始想不出来,看的题解,方法是枚举串长len的约数k,看lcp(suffix(0), suffix(k))的长度是否为n- k ,若为真则len / k即为结果. 若lcp(suffix(0), suffix(k))的长度为n- k,则将串每k位分成一段,则第1段与第2段可匹配,又可推得第2段与第3段可匹配……一直递归下去,可知每k位都是相同的,画图可看出匹配过程类似于蛇形. 用倍增算法超时,用dc3算法2.5秒勉强过. #include<cstdi

CF 504E Misha and LCP on Tree(树链剖分+后缀数组)

题目链接:http://codeforces.com/problemset/problem/504/E 题意:给出一棵树,每个结点上有一个字母.每个询问给出两个路径,问这两个路径的串的最长公共前缀. 思路:树链剖分,记录每条链的串,正反都记,组成一个大串.记录每条链对应的串在大串中的位置.然后对大串求后缀数组.最后询问就是在一些链上的查询. 树链剖分总是那么优秀.. const int N=600005; int next[N],node[N],head[N],e; void add(int u