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

2024-03-26 23:04

本文主要是介绍Codeforces Round 934 (Div. 2) ---- D. Non-Palindromic Substring --- 题解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

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());}}
}

这篇关于Codeforces Round 934 (Div. 2) ---- D. Non-Palindromic Substring --- 题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/850152

相关文章

usaco 1.2 Palindromic Squares(进制转化)

考察进制转化 注意一些细节就可以了 直接上代码: /*ID: who jayLANG: C++TASK: palsquare*/#include<stdio.h>int x[20],xlen,y[20],ylen,B;void change(int n){int m;m=n;xlen=0;while(m){x[++xlen]=m%B;m/=B;}m=n*n;ylen=0;whi

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

Codeforces Round #261 (Div. 2)小记

A  XX注意最后输出满足条件,我也不知道为什么写的这么长。 #define X first#define Y secondvector<pair<int , int> > a ;int can(pair<int , int> c){return -1000 <= c.X && c.X <= 1000&& -1000 <= c.Y && c.Y <= 1000 ;}int m

Codeforces Beta Round #47 C凸包 (最终写法)

题意慢慢看。 typedef long long LL ;int cmp(double x){if(fabs(x) < 1e-8) return 0 ;return x > 0 ? 1 : -1 ;}struct point{double x , y ;point(){}point(double _x , double _y):x(_x) , y(_y){}point op

Codeforces Round #113 (Div. 2) B 判断多边形是否在凸包内

题目点击打开链接 凸多边形A, 多边形B, 判断B是否严格在A内。  注意AB有重点 。  将A,B上的点合在一起求凸包,如果凸包上的点是B的某个点,则B肯定不在A内。 或者说B上的某点在凸包的边上则也说明B不严格在A里面。 这个处理有个巧妙的方法,只需在求凸包的时候, <=  改成< 也就是说凸包一条边上的所有点都重复点都记录在凸包里面了。 另外不能去重点。 int

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

Codeforces 482B 线段树

求是否存在这样的n个数; m次操作,每次操作就是三个数 l ,r,val          a[l] & a[l+1] &......&a[r] = val 就是区间l---r上的与的值为val 。 也就是意味着区间[L , R] 每个数要执行 | val 操作  最后判断  a[l] & a[l+1] &......&a[r] 是否= val import ja

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

C - Word Ladder题解

C - Word Ladder 题解 解题思路: 先输入两个字符串S 和t 然后在S和T中寻找有多少个字符不同的个数(也就是需要变换多少次) 开始替换时: tips: 字符串下标以0开始 我们定义两个变量a和b,用于记录当前遍历到的字符 首先是判断:如果这时a已经==b了,那么就跳过,不用管; 如果a大于b的话:那么我们就让s中的第i项替换成b,接着就直接输出S就行了。 这样

CSS实现DIV三角形

本文内容收集来自网络 #triangle-up {width: 0;height: 0;border-left: 50px solid transparent;border-right: 50px solid transparent;border-bottom: 100px solid red;} #triangle-down {width: 0;height: 0;bor