1 . 求不同子串的种类
2.长度为k的字符串的个数
3.计算所有子串的和(0-9表示)(前导0与非前导0)
4. 一种是在字符串后面添加一个字符,另一个是查询出现过最少出现K次的字串个数。
5. 恰好出现k次的字符串,至少k次的子串-至少(k+1)次的子串 =至少k次
题目来源
1445 | Lv.4 | 2016-12-10 | 534 | |
1449 | Lv.4 | 2016-12-17 | 430 | |
1457 | Lv.1 | 2016-12-24 | 324 |
http://acm.hdu.edu.cn/showproblem.php?pid=4641
http://acm.hdu.edu.cn/showproblem.php?pid=6194
#include <bits/stdc++.h>
#define LL long long
#define P pair<int, int>
#define lowbit(x) (x & -x)
#define mem(a, b) memset(a, b, sizeof(a))
const int maxn = 5500005;
#define mid ((l + r) >> 1)
#define lc rt<<1
#define rc rt<<1|1
using namespace std;
const LL mod = 1e9 + 7;
int len;
struct SAM{
int trans[maxn<<1][26], slink[maxn<<1], maxlen[maxn<<1];
// 用来求endpos
int indegree[maxn<<1], endpos[maxn<<1], rank[maxn<<1], ans[maxn<<1];
// res至少出现k次的子串个数
LL sum[maxn<<1],res=0;
int last, now, root;
inline void newnode (int v) {
maxlen[++now] = v;
mem(trans[now],0);
}
//至少出现k次的子串个数,其他情况可以不需要
inline void extend(int c,int k) {
newnode(maxlen[last] + 1);
int p = last, np = now;
sum[now]=0;
// 更新trans
while (p && !trans[p][c]) {
trans[p][c] = np;
p = slink[p];
}
if (!p) slink[np] = root;
else {
int q = trans[p][c];
if (maxlen[p] + 1 != maxlen[q]) {
// 将q点拆出nq,使得maxlen[p] + 1 == maxlen[q]
newnode(maxlen[p] + 1);
int nq = now;
memcpy(trans[nq], trans[q], sizeof(trans[q]));
sum[nq]=sum[q];///分开的节点要继承
slink[nq] = slink[q];
slink[q] = slink[np] = nq;
while (p && trans[p][c] == q) {
trans[p][c] = nq;
p = slink[p];
}
}else slink[np] = q;
}
last = np;
// 初始状态为可接受状态
endpos[np] = 1;
//查询至少出现k次子串个数
int w=np;
while(w && sum[w] < k )
{
sum[w]++;
if(sum[w]==k) res+=maxlen[w]-maxlen[slink[w]];
w=slink[w];
}
}
inline void init()
{
root = last = now = 1;
res=0;
sum[root]=0;
slink[root]=0;
mem(trans[root],0);
}
inline void getEndpos() {
// topsort
for (int i = 1; i <= now; ++i) indegree[ maxlen[i] ]++; // 统计相同度数的节点的个数
for (int i = 1; i <= now; ++i) indegree[i] += indegree[i-1]; // 统计度数小于等于 i 的节点的总数
for (int i = 1; i <= now; ++i) rank[ indegree[ maxlen[i] ]-- ] = i; // 为每个节点编号,节点度数越大编号越靠后
// 从下往上按照slik更新
for (int i = now; i >= 1; --i) {
int x = rank[i];
endpos[slink[x]] += endpos[x];
}
}
// 计算所有子串的和(0-9表示)(算前导0与不算前导0)
inline LL getSum() {
// 拓扑排序
for (int i = 1; i <= now; ++i) indegree[ maxlen[i] ]++;
for (int i = 1; i <= now; ++i) indegree[i] += indegree[i-1];
for (int i = 1; i <= now; ++i) rank[ indegree[ maxlen[i] ]-- ] = i;
mem(endpos, 0);
endpos[1] = 1; // 从根节点向后求有效的入度
for (int i = 1; i <= now; ++i) {
int x = rank[i];
for (int j = 0; j < 10; ++j) {
int nex = trans[x][j];
if (!nex) continue;
//if(maxlen[x]==0&&j==0) continue;//不算前导0
endpos[nex] += endpos[x]; // 有效入度
LL num = (sum[x] * 10 + endpos[x] * j) % mod;
sum[nex] = (sum[nex] + num) % mod; // 状态转移
}
}
LL ans = 0;
for (int i = 2; i <= now; ++i) ans = (ans + sum[i]) % mod;
return ans;
}
// 求不同的子串种类
inline LL all () {
LL ans = 0;
for (int i = root+1; i <= now; ++i) {
ans += maxlen[i] - maxlen[ slink[i] ];
}
return ans;
}
// 长度为K的字符串有多种,求出现次数最多的次数
inline void get_Maxk() {
getEndpos();
for (int i = 1; i <= now; ++i) {
ans[maxlen[i]] = max(ans[maxlen[i]], endpos[i]);
}
for (int i = len; i >= 1; --i) ans[i] = max(ans[i], ans[i+1]);
for (int i = 1; i <= len; ++i) //cout << ans[i] << endl;
printf("%d\n", ans[i]);
}
//求长度为为k的字符串有多少种
void klensum()
{
/// ans[i] 得到的是长度为 i 不同字符串的个数
getEndpos();
for(int i=root+1;i<=now;i++)
{
ans[maxlen[i]]=max(ans[maxlen[i]], endpos[i]);
}
for(int i=len;i>=1;i--)
{
ans[i]=max(ans[i], ans[i+1]);
}
for(int i=1 ; i<=len ; i++)
printf("%d\n",ans[i]);
}
}sam;
int main()
{
int n,q,k;
int t;scanf("%d",&t);
while(t--)
{
string str;int k;
cin>>k>>str;
sam.init();
n=str.size();
for(int i=0 ; i<n ; i++)
{
sam.extend(str[i]-'a' , k);
}
LL sum1=sam.res;
sam.init();
for(int i=0 ; i<n ; i++)
{
sam.extend(str[i]-'a' , k+1);
}
LL sum2=sam.res;
printf("%lld\n",sum1-sum2);
}
}
int main()
{
int n;
while(~scanf("%d",&n))
{
sam.init();
for(int i=1 ; i<=n; i++)
{
scanf("%s",str);
len=strlen(str);
for(int j=0 ; j<len ; j++)
{
sam.extend(str[j]-'0',0);
}
sam.extend(10,0); //分割开字符串
}
printf("%d\n",sam.getSum());
}
}