重复旋律5
求不同子串个数,SAM后统计所有节点的mx-mi+1即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int SZ = 26;
const int maxn = 1e6+5;
namespace SAM{
struct SamNode{
int trans[SZ],slink,mi,mx;}sam[maxn<<1];
int tot; char s[maxn];
int newnode(int mi,int mx,int *trans,int slink) {
sam[tot].mi=mi;
sam[tot].mx=mx;
sam[tot].slink=slink;
trans?memcpy(sam[tot].trans,trans,SZ*sizeof(int)):memset(sam[tot].trans,-1,SZ*sizeof(int));
return tot++;
}
int append(int ch,int u) {
int c=s[ch]-'a';
int z=newnode(-1,sam[u].mx+1,0,-1);
int v=u;
while(v!=-1&&sam[v].trans[c]==-1) {
sam[v].trans[c]=z;
v=sam[v].slink;
}
if(v==-1) {
sam[z].slink=0;
sam[z].mi=1;
} else {
int x=sam[v].trans[c];
if(sam[v].mx+1==sam[x].mx) {
sam[z].mi=sam[x].mx+1;
sam[z].slink=x;
} else {
int y=newnode(-1,sam[v].mx+1,sam[x].trans,sam[x].slink);
sam[x].slink=y;
sam[x].mi=sam[y].mx+1;
sam[z].slink=y;
sam[z].mi=sam[y].mx+1;
while(v!=-1&&sam[v].trans[c]==x) {
sam[v].trans[c]=y;
v=sam[v].slink;
}
sam[y].mi=sam[sam[y].slink].mx+1;
}
}
return z;
}
};
int main() {
scanf("%s",SAM::s+1);
int u=SAM::newnode(0,0,0,-1);
int n=strlen(SAM::s+1);
for(int i=1;i<=n;i++) {
u=SAM::append(i,u);
}
ll ans=0;
for(int i=1;i<SAM::tot;i++) {
ans=ans+SAM::sam[i].mx-SAM::sam[i].mi+1;
}
printf("%lld\n",ans);
return 0;
}
重复旋律6
统计长度为1~n的所有子串的最多出现次数。
相当于求SAM每个节点的endpos集合大小,来更新该长度为节点mi~mx的ans(可以直接更新ans[mx],然后取后缀max,长度短的一定比长的出现次数多)
建slink_tree后dp,将节点分为两种:
1)仅右子节点集合构成
2)1)+ 字符串前缀(不可能出现在子节点中)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 6;
const int SZ = 26;
namespace SAM {
struct SamNode{
int mi,mx,slink,trans[SZ];}sam[maxn<<1];
char s[maxn]; int tot; ll dp[maxn<<1],ans[maxn<<1];
int newnode(int mi,int mx,int *trans,int slink) {
sam[tot].mi=mi; sam[tot].mx=mx; sam[tot].slink=slink;
trans?memcpy(sam[tot].trans,trans,SZ*sizeof(int)):memset(sam[tot].trans,-1,SZ*sizeof(int));
return tot++;
}
int append(int i,int u) {
char c=s[i]-'a';
int z=newnode(-1,sam[u].mx+1,0,-1); dp[z]=1;
int v=u;
while(v!=-1&&sam[v].trans[c]==-1) {
sam[v].trans[c]=z;
v=sam[v].slink;
}
if(v==-1) {
sam[z].slink=0;
sam[z].mi=1;
} else {
int x=sam[v].trans[c];
if(sam[v].mx+1==sam[x].mx) {
sam[z].slink=x;
sam[z].mi=sam[x].mx+1;
} else {
int y=newnode(-1,sam[v].mx+1,sam[x].trans,sam[x].slink); dp[y]=0;
sam[x].slink=y; sam[x].mi=sam[y].mx+1;
sam[z].slink=y; sam[z].mi=sam[y].mx+1;
while(v!=-1&&sam[v].trans[c]==x) {
sam[v].trans[c]=y;
v=sam[v].slink;
}
sam[y].mi=sam[sam[y].slink].mx+1;
}
}
return z;
}
vector<int> G[maxn<<1];
void slink_tree() {
for(int i=1;i<tot;i++) G[i].clear();
for(int i=1;i<tot;i++) G[sam[i].slink].push_back(i);
}
void dfs(int u) {
for(int v:G[u]) {
dfs(v);
dp[u]+=dp[v];
}
}
void calu() {
slink_tree();
dfs(0);
for(int i=1;i<tot;i++) ans[sam[i].mx]=max(ans[sam[i].mx],dp[i]);
int n=strlen(s+1);
for(int i=n-1;i>=1;i--) ans[i]=max(ans[i+1],ans[i]);
for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
}
};
int main() {
scanf("%s",SAM::s+1);
int n=strlen(SAM::s+1),u=SAM::newnode(0,0,0,-1);
for(int i=1;i<=n;i++) {
u=SAM::append(i,u);
}
SAM::calu();
}
重复旋律7
(SAM 本身的转移函数trans为一个)
求多个串(‘0’~‘9’)的不同子串之和。
考虑如何求单个串的不同子串之和:
假如所有能够通过读入字符转移到状态i的状态的答案已经求得,那么状态i的答案即为 sigma(ANSj10+NUMjc)。拓扑序dp即可
多个串的情况则可以通过一个字符将他们链接起来,那么在SAM中,只需将每个状态代表的子串中出现这个新加入的字符忽略掉就可以。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6 + 6;
const int mod = 1e9 + 7;
const int SZ = 11;
namespace SAM {
struct SamNode{
int mi,mx,slink,trans[SZ];}sam[maxn<<1];
char s[maxn]; int tot;
int newnode(int mi,int mx,int *trans,int slink) {
sam[tot].mi=mi; sam[tot].mx=mx; sam[tot].slink=slink;
trans?memcpy(sam[tot].trans,trans,SZ*sizeof(int)):memset(sam[tot].trans,-1,SZ*sizeof(int));
return tot++;
}
int append(int i,int u) {
char c=s[i]-'0';
int z=newnode(-1,sam[u].mx+1,0,-1);
int v=u;
while(v!=-1&&sam[v].trans[c]==-1) {
sam[v].trans[c]=z;
v=sam[v].slink;
}
if(v==-1) {
sam[z].slink=0;
sam[z].mi=1;
} else {
int x=sam[v].trans[c];
if(sam[v].mx+1==sam[x].mx) {
sam[z].slink=x;
sam[z].mi=sam[x].mx+1;
} else {
int y=newnode(-1,sam[v].mx+1,sam[x].trans,sam[x].slink);
sam[x].slink=y; sam[x].mi=sam[y].mx+1;
sam[z].slink=y; sam[z].mi=sam[y].mx+1;
while(v!=-1&&sam[v].trans[c]==x) {
sam[v].trans[c]=y;
v=sam[v].slink;
}
sam[y].mi=sam[sam[y].slink].mx+1;
}
}
return z;
}
queue<int>q; int in[maxn<<1];
ll num[maxn<<1],dp[maxn<<1];
void top_valid() {
for(int i=0;i<tot;i++) {
for(int j=0;j<SZ;j++) if(sam[i].trans[j]!=-1)
in[sam[i].trans[j]]++;
}
while(!q.empty()) q.pop();
q.push(0) ; num[0]=1;
while(!q.empty()) {
int f=q.front();q.pop();
for(int i=0;i<SZ;i++) {
int v=sam[f].trans[i];
if(v==-1) continue;
in[v]--;
if(!in[v]) q.push(v);
if(i!=10) (num[v]+=num[f])%=mod;
}
}
}
void top_ans() {
for(int i=0;i<tot;i++) {
for(int j=0;j<SZ;j++) if(sam[i].trans[j]!=-1)
in[sam[i].trans[j]]++;
}
while(!q.empty()) q.pop();
q.push(0); dp[0]=0;
while(!q.empty()) {
int f=q.front();q.pop();
for(int i=0;i<SZ;i++) {
int v=sam[f].trans[i];
if(v==-1) continue;
in[v]--;
if(!in[v]) q.push(v);
if(i!=10) dp[v]=(dp[v]+dp[f]*10%mod+num[f]*i)%mod;
}
}
}
void calu() {
top_valid();
top_ans();
ll ans=0;
for(int i=1;i<tot;i++) ans=(ans+dp[i])%mod;
printf("%lld\n",ans);
}
};
char s[maxn];
int main() {
int n,cnt=0;scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%s",s);
int len=strlen(s);
for(int j=0;j<len;j++) SAM::s[++cnt]=s[j];
SAM::s[++cnt]='9'+1;
}
int u=SAM::newnode(0,0,0,-1);
for(int i=1;i<=cnt;i++) {
u=SAM::append(i,u);
}
SAM::calu();
}