时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
总所周知的是,小Y对于字符串之类的题目总是很得心应手的,这一天漂亮的学妹问了小Y一个题目,问两个字符串怎样进行字典序大小比较,当然啦,这种题目对于小Y来说简直是太太太太太简单的啦~~~~于是小Y只花了一点时间就和学妹说清楚了。
不过为了和学妹获得更多的相处时间,小Y灵机一动,给学妹提了一道看似简单的问题,问给定一两个字符串a,b,问a里面有多少个子串字典序小于b。
学妹算着算着就迷糊了,于是她向你求助,如果你帮助她解决这个问题,那么可能漂亮的学妹会考虑一下让你做她男朋友哦~~~(题目保证输入全部为小写英文字母)
输入描述:
输入两个字符串a,b(1<=|b|<|a|<=200000)。
输出描述:
输出一个数,表示a的子串里面有多少个串字典序小于b串。
示例1
输入
aababac ba
输出
21
示例2
输入
bcefeghijklmn df
输出
25
题解:扩展KMP的应用。可以把题意转化为a的所有后缀与b的最长匹配(扩展KMP求解),然后比较字典序求和。一位大佬的代码,orz!
扩展KMP:定义母串S,和字串T,设S的长度为n,T的长度为m,求T与S的每一个后缀的最长公共前缀,也就是说,设extend数组,extend[i]表示T与S[i,n-1]的最长公共前缀,要求出所有extend[i](0<=i<n)。
AC代码:
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 2e5 + 10;
char s[MAXN], p[MAXN]; //主串 模式串
int n, m; //s长度 p长度
int exnex[MAXN], exten[MAXN]; //扩展next数组 s从i为起点的后缀与p的最长公共前缀
void getexnext()
{
int i = 0, j;
exnex[0] = m; //0号位为长度
while (i + 1 < m && p[i] == p[i + 1]) //计算第一项
i++;
exnex[1] = i;
int k = 1 + i, po = 1; //k最远到达点 po最远到达点的起点
for (i = 2; i < m; i++)
{
if (exnex[i - po] < k - i) //因k~po == 0~k-po 则i~k == i-po~k-po(取前者后一部分)
exnex[i] = exnex[i - po]; //如果exnet[i-po]不足k-i长度则s[i - po + exnet[i-po]] != s[i + exnet[i-po]]
else
{
j = k - i;//如果长度相等或更长因k之后为未探索区域 暂定为k - i之后继续匹配
if (j < 0) //如果i本身就超过k
j = 0;
while (i + j < m && p[i + j] == p[j])
j++;
exnex[i] = j;
k = i + j, po = i; //更新记录
}
}
}
void exkmp()
{
getexnext(); //计算exnext
int i = 0, j;
while (s[i] == p[i] && i < m && i < n) //计算第零项
i++;
exten[0] = i;
int k = i, po = 0;
for (i = 1; i < n; i++) //从1开始计算
{
if (exnex[i - po] < k - i)
exten[i] = exnex[i - po]; //exten
else
{
j = k - i;
if (j < 0)
j = 0;
while (i + j < n && j < m && s[i + j] == p[j]) //n m s
j++;
exten[i] = j; //exten
k = i + j, po = i;
}
}
}
int main()
{
cin >> s >> p;
n = strlen(s); //按需求添加
m = strlen(p);
exkmp();
long long ans = 0;
for (int i = 0; i < n; i++)
{
ans += min(exten[i], m - 1); //相同部分的每一个前缀都是 不能和b相等
if (s[i + exten[i]] < p[exten[i]]) //不同部分比较字典序 如果小则有n - i - exten[i]个
ans += n - i - exten[i];
}
cout << ans << endl;
return 0;
}
扩展KMP模板:
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e3 + 10;
char s[MAXN], p[MAXN]; //主串 模式串
int n, m; //s长度 p长度
int exnex[MAXN], exten[MAXN]; //扩展next数组 s从i为起点的后缀与p的最长公共前缀
void getexnext()
{
int i = 0, j;
exnex[0] = m; //0号位为长度
while (i + 1 < m && p[i] == p[i + 1]) //计算第一项
i++;
exnex[1] = i;
int k = 1 + i, po = 1; //k最远到达点 po最远到达点的起点
for (i = 2; i < m; i++)
{
if (exnex[i - po] < k - i) //因k~po == 0~k-po 则i~k == i-po~k-po(取前者后一部分)
exnex[i] = exnex[i - po]; //如果exnet[i-po]不足k-i长度则s[i - po + exnet[i-po]] != s[i + exnet[i-po]]
else
{
j = k - i;//如果长度相等或更长因k之后为未探索区域 暂定为k - i之后继续匹配
if (j < 0) //如果i本身就超过k
j = 0;
while (i + j < m && p[i + j] == p[j])
j++;
exnex[i] = j;
k = i + j, po = i; //更新记录
}
}
}
void exkmp()
{
getexnext(); //计算exnext
int i = 0, j;
while (s[i] == p[i] && i < m && i < n) //计算第零项
i++;
exten[0] = i;
int k = i, po = 0;
for (i = 1; i < n; i++) //从1开始计算
{
if (exnex[i - po] < k - i)
exten[i] = exnex[i - po]; //exten
else
{
j = k - i;
if (j < 0)
j = 0;
while (i + j < n && j < m && s[i + j] == p[j]) //n m s
j++;
exten[i] = j; //exten
k = i + j, po = i;
}
}
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
cin >> s >> p;
n = strlen(s); //按需求添加
m = strlen(p);
return 0;
}
/*
aaaabaaaaa
*/