【洛谷】P8715 [蓝桥杯 2020 省 AB2] 子串分值 的题解
传送门
思路
这题是典型的线性相加变成多项乘法和的思想。
以每一位的字符为子串子元素的只有那么几个,把每一个位字符的贡献度求出来,然后求个和,这样就得到结果了。
出现相同的字符的时候,把每一位的字符前一次出现的位置 p r e [ i ] pre[i] pre[i] 和后一次出现的位置 s u f [ i ] suf[i] suf[i] 找出来,避开这些地方去构造子串。
对于某个字符 s [ i ] s[i] s[i],避开其前后出现的位置之后,就可以利用区间 p r e [ i ] pre[i] pre[i], s u f [ i ] suf[i] suf[i] 的字符串去构造 s [ i ] s[i] s[i] 一定有贡献的子串了。
在这个区间里随便找出一个含有 s [ i ] s[i] s[i] 的子串,那这个子串里 s [ i ] s[i] s[i] 都会产生 1 1 1 的贡献。
那 s [ i ] s[i] s[i] 对整个字符串产生的贡献数就是前面得到的区间里能够生成的包含 s [ i ] s[i] s[i] 的子串个数。
而这个个数,就是 ( i − p r e [ i ] ) × ( s u f [ i ] − i ) (i - pre[i]) \times (suf[i] - i) (i−pre[i])×(suf[i]−i)
代码
#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
namespace fastIO {
inline int read() {
register int x = 0, f = 1;
register char c = getchar();
while (c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
inline void write(ll x) {
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
return;
}
}
using namespace fastIO;
string s;
int pre[100005], suf[100005], temp[35];
int main() {
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin >> s;
int len = s.length();
for(int i = 0; i < 26; i ++) {
temp[i] = -1;
}
for(int i = 0; s[i]; i ++) {
pre[i] = temp[s[i]-'a'];
temp[s[i] - 'a'] = i;
}
for(int i = 0; i < 26; i ++) {
temp[i] = len;
}
for(int i = len - 1; i >= 0; i --) {
suf[i] = temp[s[i] - 'a'];
temp[s[i] - 'a'] = i;
}
ll res = 0;
for(int i = 0; i < len; i ++) {
res += (i - pre[i]) * (suf[i] - i);
}
write(res);
return 0;
}