Codeforces Round 934 (Div. 2) ---- D. Non-Palindromic Substring --- 题解

本文介绍了如何使用线段树和Manacher算法解决关于字符串的回文特性问题,包括确定k好字符串的f值,针对给定字符串区间进行查询。主要讨论了单字符和双字符循环字符串的情况,以及如何利用这两种方法快速计算特定区间的f值。
摘要由CSDN通过智能技术生成

目录

D. Non-Palindromic Substring :

题目描述:

思路解析:

下面给出两种代码的代码实现:

代码一:线段树实现hash判断回文字符串

代码二:manacher判断回文字符串


D. Non-Palindromic Substring :

题目描述:

假设有一个字符串长度为n, 如果这个字符串没有长度为k的回文子串,那么称这个字符串为k好字符串,现在定义f(str) = k1+k2+......,如果字符串str是一个k1字符串,k2字符串......

现在给出一个长度为n的字符串和q次查询,每次查询区间 [li,ri] 问这个区间对应的字符串的f值为多少。

思路解析:

任意一个字符串一定不是1好字符串,因为每个长度为1的子串都是回文串。

假设现在字符串长度为n,k为偶数,什么情况才能满足字符串不是k好字符串 假设k==4

那么字符串有形如 a1a2a3a4a5....的结构 假设 (a1 a2 a3 a4) 和 (a2 a3 a4 a5) 是回文的,那么可以推出a1 == a2 == a3 == a4 == a5,即只有一个字符重复的字符串才可能出现 不是 k好字符串 (k为偶数) 且此时,奇数也不为好字符串。

假设现在字符串长度为n,k为奇数,什么情况才能满足字符串不是k好字符串,假设k==3

那么字符串有形如 a1a2a3a4a5.....的结构 假设(a1a2a3) 和 (a2a3a4) 回文的,那么可以推出 a1 == a3 == a5.... a2 == a4,即只要满足是两个字符循环的字符串 (如ababab...)。可以发现此时任意长度小于等于 n的奇数k, 字符串都不是k好字符串,而且单字符循环可以看作此情况的特殊情况。

那么现在整个查询情况可以分为三类:

(1) 单字符循环的字符串, 此时f值为0

(2) 双字符循环的字符串,此时f值为 2 + 4 + ..... + m  (m <= n),如果n长度为奇数则不加n

(3) 啥也不是 此时f值 2 + 3 + 4 + ..... + n, 如果长度为n的字符串是回文字符串则减去n

则(2)f值可看作 2 + 4 + ... + m (m < n) (3) f值可看作  2 + 3 + ....+ (n-1)  这两种情况都去特判一下整个字符串是否为回文字符串即可。 这里判断字符串是否回文字符串有两种做法,hash或者manacher。

// 这里判断循环字符串可以利用边界判断的方法,详细思路请看代码,自己模拟即可

下面给出两种代码的代码实现:

代码一:线段树实现hash判断回文字符串

import java.io.*;
import java.math.BigInteger;
import java.util.*;

public class Main {
    static int inf = (int) 2e7;
    static int[] ff = new int[26];
    static SegTree tree;
    public static void main(String[] args) throws IOException {
        pow = new long[MAXN];
        pow[0] = 1;
        for (int i = 1; i < MAXN; i++) {
            pow[i] = pow[i-1] * base % mod;
        }
        Random random = new Random();
        for (int i = 0; i < 26; i++) {
            ff[i] = random.nextInt(235879);
        }
        int t = f.nextInt();
        tree = new SegTree();
        while (t > 0) {
            solve();
            t--;
        }

        w.flush();
        w.close();
        br.close();
    }
    public static void solve() {
        n = f.nextInt();
        int q = f.nextInt();
        s = (" " + f.next()).toCharArray();
        TreeSet<Integer> s1 = new TreeSet<>();
        TreeSet<Integer> s2 = new TreeSet<>(); // 利用s1判断单字符循环, s2判断双字符循环

        for (int i = 1; i < n; i++) {
            if (s[i+1] != s[i]) s1.add(i);
            if (i+2 <= n && s[i] != s[i+2]) s2.add(i);
        }
        s1.add(n+1);
        s2.add(n+1);
        tree.build(1, 1, n);
        for (int i = 0; i < q; i++) {
            int l = f.nextInt();
            int r = f.nextInt();
            int len = r - l + 1;
            long ans = 0;
            if (l == r) {w.println(0); continue;}
            int a = s1.ceiling(l);
            if (a >= r) {w.println(0); continue;}
            else {
                a = s2.ceiling(l);
                if (a >= r - 1) ans = (long) ((len - 1) / 2) * ((len - 1) / 2 + 1);
                else ans = (long) len * (len - 1) / 2 - 1;
            }
            long[] b = tree.qry(1, l, r);
            if (b[0] != b[1]) ans += len;
            w.println(ans);
        }
    }

    static int base = 29;
    static int mod = (int) 1e9 + 9;
    static long[] pow;
    static int MAXN = (int) 2e5 + 5;
    static char[] s;
    static int n;
    public static class Node{
        int l,r;
        long[] sum = new long[2];
    }

    public static class SegTree{
        Node[] t = new Node[MAXN * 4];
        public SegTree(){
            for (int i = 0; i < MAXN * 4; i++) {
                t[i] = new Node();
            }
        }

        public void build(int root, int l, int r){
            t[root].l = l;
            t[root].r=r;
            if (t[root].l == t[root].r){
                t[root].sum[0] = ff[s[l] - 'a'];
                t[root].sum[1] = ff[s[l] - 'a'];
                return;
            }
            int ch = root << 1;
            int mid = (l + r) >> 1;
            build(ch, l, mid); build(ch|1, mid+1, r);
            update(root);
        }
        void update(int root){
            int ch = root << 1;
            int len1 = t[ch|1].r - t[ch|1].l + 1;
            int len2 = t[ch].r - t[ch].l + 1;
            t[root].sum[0] = (t[ch].sum[0] * pow[len1] % mod + t[ch|1].sum[0]) % mod;
            t[root].sum[1] = (t[ch].sum[1] + t[ch|1].sum[1] * pow[len2] % mod) % mod;
        }
        long[] qry(int root, int l, int r){
            if (t[root].l == l && t[root].r == r){
                return t[root].sum;
            }
            int ch = root << 1; int mid = (t[root].l + t[root].r) >> 1;
            if (r <= mid) return qry(ch, l, r);
            else if (l > mid) return qry(ch|1, l, r);
            else {
                long[] a = qry(ch, l, mid);
                long[] b = qry(ch|1, mid+1, r);
                long[] tmp = new long[2];
                int len1 = r - mid;
                int len2 = mid - l + 1;
                tmp[0] = (a[0] * pow[len1] % mod + b[0]) % mod;
                tmp[1] = (a[1] + b[1] * pow[len2] % mod) % mod;
                return tmp;
            }
        }
    }

    static PrintWriter w = new PrintWriter(new OutputStreamWriter(System.out));
    static Input f = new Input(System.in);
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    static class Input {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public Input(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public String nextLine() {
            String str = null;
            try {
                str = reader.readLine();
            } catch (IOException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            return str;
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public Double nextDouble() {
            return Double.parseDouble(next());
        }

        public BigInteger nextBigInteger() {
            return new BigInteger(next());
        }
    }
}

代码二:manacher判断回文字符串

import java.io.*;
import java.math.BigInteger;
import java.util.*;

public class Main {
    static int inf = (int) 2e7;
    static int[] ff = new int[26];
    public static void main(String[] args) throws IOException {
        Random random = new Random();
        for (int i = 0; i < 26; i++) {
            ff[i] = random.nextInt(235879);
        }
        int t = f.nextInt();
        while (t > 0) {
            solve();
            t--;
        }

        w.flush();
        w.close();
        br.close();
    }

    public static void solve() {
        int n = f.nextInt();
        int q = f.nextInt();
        String str = f.next();
        char[] s = (" " + str).toCharArray();
        TreeSet<Integer> s1 = new TreeSet<>();
        TreeSet<Integer> s2 = new TreeSet<>();
        for (int i = 1; i < n; i++) {
            if (s[i+1] != s[i]) s1.add(i);
            if (i+2 <= n && s[i] != s[i+2]) s2.add(i);
        }
        s1.add(n+1);
        s2.add(n+1);
        int[] p = manacher(str.toCharArray());
        for (int i = 0; i < q; i++) {
            int l = f.nextInt();
            int r = f.nextInt();
            int len = r - l + 1;
            long ans = 0;
            if (l == r) {w.println(0); continue;}
            int a = s1.ceiling(l);
            if (a >= r) {w.println(0); continue;}
            else {
                a = s2.ceiling(l);
                if (a >= r - 1) ans = (long) ((len - 1) / 2) * ((len - 1) / 2 + 1);
                else ans = (long) len * (len - 1) / 2 - 1;
            }
            if (p[l + r - 1] < len) ans += len;
            w.println(ans);
        }
    }

    public static int[] manacher(char[] s){
        int n = s.length;
        char[] a = new char[2 * n + 1];
        for (int i = 0; i < n; i++) {
            a[2 * i] = '#';
            a[2 * i + 1] = s[i];
        }
        a[2 * n] = '#';
        return manacher_odd(a);
    }

    public static int[] manacher_odd(char[] s){
        int len = 0;
        int n = s.length;
        int[] p = new int[n];
        int r = 0;
        int c = 0;
        for (int i = 0; i < n; i++) {
            len = r > i ? Math.min(p[2 * c - i], r - i) : 1;
            while (len + i < n && i - len >= 0 && s[i + len] == s[i - len]) len++;
            if (len + i > r){
                c = i;
                r = len + i;
            }
            p[i] = len;
        }
        for (int i = 0; i < n; i++) {
            p[i]--;
        }
        return p;
    }

    static PrintWriter w = new PrintWriter(new OutputStreamWriter(System.out));
    static Input f = new Input(System.in);
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    static class Input {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public Input(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }

        public String nextLine() {
            String str = null;
            try {
                str = reader.readLine();
            } catch (IOException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            return str;
        }

        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

        public Double nextDouble() {
            return Double.parseDouble(next());
        }

        public BigInteger nextBigInteger() {
            return new BigInteger(next());
        }
    }
}

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Studying~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值