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_{i,s_i}-c_{l-1,s_i}-1>c_{i,x}-c_{l-1,x} \]

\[c_{i,s_i}-c_{i,x}-1>c_{l-1,s_i}-c_{l-1,x} \]

\[1-1>c_{l-1,s_i}-c_{l-1,x} \]

\[0>c_{l-1,s_i}-c_{l-1,x} \]

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

posted @ 2022-08-01 12:07  lsj2009  阅读(30)  评论(0)    收藏  举报