题意:
已知26个小写字母有各自的权值(正,负,或0)
现在给出一个字符串,长度<=1e5
问这个字符串有多少个子串满足:
开头的字母和结尾的字母一样
字符串除了开头和结尾的字母外,其余的字母的权值和为0
本来是一道水题,一看就知道大体的思路了,结果硬是搞了一个多小时
先是用set,发现超时了,改为用map
思路:
1.我们先预处理这个字符串的权值的前缀和
sum[i]表示从字符串的起点到i的权值和
2.我们要找到的子串,设以l开头和r结尾,则有
str[r] == str[l] 和 sum[r-1] - sum[l] == 0
即:sum[r] - sum[l] == val[str[r] - 'a']
那么我们只要记录:
(char,sum)以0为开头,char为结尾,权值和为sum的子串的个数即可
实现:用一个map
遍历一遍字符串,对于每一个(char,sum),找到前面出现多少(char,sum-val[char-'a'])
然后更新res,并且map[(char,sum)]++
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <set> #include <map> #include <cstdlib> #define LL long long #define fir first #define sec second using namespace std; int val[26]; LL sum[100000+5]; char str[100000+5]; map< pair<char,LL>,int > ms; map< pair<char,LL>,int > :: iterator it; void solve() { for(int i=0;i<26;i++){ scanf("%d",&val[i]); } scanf("%s",str); int len = strlen(str); ms.clear(); LL res = 0; sum[0] = 0; for(int i=1;i<=len;i++){ sum[i] = sum[i-1] + val[str[i-1] - 'a']; } pair<char,LL> pr; LL tmp; for(int i=1;i<=len;i++){ pr = make_pair(str[i-1],sum[i]); it = ms.find(make_pair(pr.fir,pr.sec - val[pr.fir-'a'])); if(it != ms.end()) res += it->sec; ms[pr]++; } //printf("%lld ",res); printf("%I64d ",res); return ; } int main() { solve(); return 0; }