CF1673B solution
Problem
给定一字符串 \(s\),问是否有对于一个 \(s\) 的子串 \(t\) 而言,对于有 \(a\) 在 \(t\) 中的出现次数与 \(b\) 在 \(t\) 中的出现次数之差大于 \(1\),其中 \(a,b\in s\)。
link->https://codeforces.com/contest/1673/problem/B
Solution
下文我们规约定 \(n\) 为 \(|s|\)。
最暴力的方法是枚举 \(s\) 的所有子串,其个数在 \(n^2\) 这一数量级,然后再一一遍历,将其中每个字符一一装桶,统计个数,最终判断是否有两个字符均在 \(s\) 中出现过,且出现个数差值大于 \(1\),这一部分遍历时间复杂度为 \(\Theta(n)\),判断是否合法复杂度为 \(26^2\)。时间复杂度总为 \(\Theta(n^3)\) 左右。
当然,我们可以进行优化:我们可以预先统计出每个字符在某一位置 \(i\) 之前出现的总数,记为 \(c_{i,x}\),其中 \(x\) 表示字符,\(i\) 表示规定的位置。
则字符 \(x\) 在区间 \([l,r]\) 内出现的个数为 \(c_{r,x}-c_{l-1,x}\)。这样,我们只需要枚举子串然后再一一枚举两个字符是否合法即可。时间复杂度为 \(\Theta(26^2n^2)\)。
然而,哪怕经过优化,我们任然无法通过此题,那么该怎么办呢?
仔细想一想,如果我们遍历到点 \(i\),那么如果有一个字符 \(x\) 的当前出现次数与 \(s_i\) 的出现次数(现在我们不需要再枚举两个字符,因为只有 \(s_i\) 的出现次数发生了变化,所以枚举的其中一个字符一定是 \(s_i\))相差 \(1\)(如果相差次数大于 \(1\) 那么可以直接输出 NO 并退出),那么,我们要取哪个左端点 \(l\) 才能满足条件呢?
我们要求:
而 \(c_{l-1,s_i}-c_{l-1,x}\) 不可能大于 \(1\)(因为如果大于,那么在之前就退出了),则他一定等于 \(1\)。我们可以使用一个二元布尔数组判断这样的左端点是否出现过,即用 \((a,b)=1\) 表示有一个 \(i\) 满足 \(0>c_{l-1,s_i}-c_{l-1,x}\)。
当然,在枚举 \(x\) 时,我们还需要判断 \(x\) 是否出现过。
Code
#include<bits/stdc++.h> //qwq
#define pd push_back
#define pb pop_back
#define mk make_pair
//#define int long long
#define PII pair<int,int>
#define _for(a,b,c) for(int a=b;a<=c;a++)
#define _rep(a,b,c) for(int a=b;a>=c;a--)
#define _go(i,u) for(int i=head[u];i;i=Edge[i].nxt)
using namespace std;
namespace I {
template <typename T> void read(T& x) {
x=0; T f=1; char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch&15); ch=getchar(); }
x=x*f; return;
}
template <typename T,typename ...Arg> void read(T& x,Arg& ...arg) {
read(x); read(arg...);
}
}
namespace Math {
template <typename T> T power(T a,T b,T MOD) {
T ans=1; while(b) { if(b&1) ans=ans*a%MOD; a=a*a%MOD; b>>=1; } return ans%MOD;
}
template <typename T> T gcd(T a,T b) {
if(b) return gcd(b,a%b); return a;
}
template <typename T> T lcm(T a,T b) {
return a/gcd(a,b)*b;
}
template <typename T> T inv(T n,T p) {
return power(n,p-2);
}
const int Test[]={0,2,3,5,7,11,13,17,19,23,29};
template <typename T> bool is_prime(T n) {
if(n<2) return 0;
T t=n-1,k=0; while((t+1)&1) ++k,t>>=1;
_for(i,1,10) {
if(n==Test[i]) return 1;
T s=power(Test[i],t,n); T tmp=s;
_for(j,1,k) {
tmp=s*s%n; if(tmp==1&&s!=1&&s!=n-1) return 0; s=tmp;
}
if(s!=1) return 0;
}
return 1;
}
}
const int N=2e5+5;
char s[N];
bool used[30][30],have[30];
int cnt[30];
signed main() {
int T;
I::read(T);
while(T--) {
memset(used,0,sizeof(used));
memset(cnt,0,sizeof(cnt));
memset(have,0,sizeof(have));
scanf("%s",s+1);
int n=strlen(s+1);
bool flag=0;
_for(i,1,n) have[s[i]-'a']=1;
_for(i,1,n) {
++cnt[s[i]-'a'];
int c=s[i]-'a';
_for(j,0,'z'-'a') {
if(!have[j]) continue;
if(cnt[c]-cnt[j]>1||cnt[j]-cnt[c]>1)
flag=1;
if(cnt[c]-cnt[j]==1) {
used[c][j]=1;
if(used[j][c])
flag=1;
}
if(cnt[j]-cnt[c]==1) {
used[j][c]=1;
if(used[c][j])
flag=1;
}
}
}
puts(flag? "NO":"YES");
}
return 0;
}

浙公网安备 33010602011771号