题目大意
给一个字符串,和若干个区间([l,r]),区间([l,r])为一个子串(s),你可以构造一个串(t),它是(s)字符重排构成的(可以是任意顺序)。问你能否存在一个串(t),无论怎样将它分成两个以上的段,至少有一个段内的字符无论如何排列都不可能与(s)对应段相同。
比如这个串,(s)是固定不变的,(t)是把(s)重排得到的,把它们分成一定大小的段后,(t)每段的字符经过重排都能和(s)对应段一样。
分析
1.如果是长度为(1)的区间,那么肯定是符合要求的,因为它没法分成两个以上的段。
2.如果是长度大于(1)的区间,那么有两种情况。如果是首尾字符不同,那么肯定是符合要求的,因为要分成两个以上的段,它的首尾元素必定不处于同一段内,那么只要将他们调换一下就行了。
3.如果长度大于(1)而且首尾也相同呢?那么我们构造的(t)就必须满足两个条件。
- 如果将(t)的末尾元素单独切开,那么要么(t)末尾元素左边的区间和(s)的对应段不同,要么(t)末位元素和(s)的末尾元素不同。
- 如果将(t)的首元素单独切开,那么要么(t)首元素右边的区间和(s)的对应段不同,要么(t)首元素和(s)的末尾元素不同。
这样的话最少就需要(3)个字符,为什么是(3)个?如果有(3)个不同字符,把两个与首尾元素不同的字符分别放到首尾位置就能满足上面的条件。如果是(2)个的话,显然不行。
具体实现
上面的前两种情况只需要简单判断一下就行了,对于第三种情况,如何判断区间内的字符种类呢?很简单,只需要求一个前缀和,存放每个位置每种字符的数量,计算区间两端的字符数量不相等的字符种类就行了。
代码
const int maxn = 2e5+10;
char str[maxn] = "#";
int pre[maxn][26];
int main(void) {
scanf("%s", str+1);
int len = strlen(str);
for (int i = 1; i<len; ++i) {
++pre[i][str[i]-'a'];
for (int j = 0; j<26; ++j) pre[i][j] += pre[i-1][j];
}
int t;
scanf("%d", &t);
while(t--) {
int l, r;
scanf("%d%d", &l, &r);
if (l-r==0 || str[l]!=str[r]) printf("Yes
");
else {
int cnt = 0;
for (int i = 0; i<26; ++i)
if (pre[l-1][i]!=pre[r][i]) ++cnt;
printf(cnt>=3? "Yes
":"No
");
}
}
return 0;
//https://www.cnblogs.com/shuitiangong/ 让我看看哪个憨批网站爬我博客(笑
}