【BZOJ 4180】 4180: 字符串计数 (SAM+二分+矩阵乘法)

4180: 字符串计数

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 164  Solved: 75

Description

SD有一名神犇叫做Oxer,他觉得字符串的题目都太水了,于是便出了一道题来虐蒟蒻yts1999。

他给出了一个字符串T,字符串T中有且仅有4种字符 ‘A‘, ‘B‘, ‘C‘, ‘D‘。现在他要求蒟蒻yts1999构造一个新的字符串S,构造的方法是:进行多次操作,每一次操作选择T的一个子串,将其加入S的末尾。

对于一个可构造出的字符串S,可能有多种构造方案,Oxer定义构造字符串S所需的操作次数为所有构造方案中操作次数的最小值。

Oxer想知道对于给定的正整数N和字符串T,他所能构造出的所有长度为N的字符串S中,构造所需的操作次数最大的字符串的操作次数。

蒟蒻yts1999当然不会做了,于是向你求助。

Input

第一行包含一个整数N,表示要构造的字符串长度。

第二行包含一个字符串T,T的意义如题所述。

Output

输出文件包含一行,一个整数,为你所求出的最大的操作次数。

Sample Input

5

ABCCAD

Sample Output

5

HINT

【样例说明】

例如字符串"AAAAA",该字符串所需操作次数为5,不存在能用T的子串构造出的,且所需操作次数比5大的字符串。

【数据规模和约定】

对于100%的数据,1 ≤ N ≤ 10^18,1 ≤ |T| ≤ 10^5。

Source

By yts1999

【分析】

  好题啊。我没想到。。

  要用一种稍微转化一下的思维?

  要算n最多操作次数,可以二分答案,然后询问操作次数为x的区间最小长度是多少。【我个人觉得这样想也不简单啊。

  考虑如果S确定的话,其实是贪心的,匹配到不能匹配的时候断掉,成为新的一段。

  在SAM上就是没有儿子的后继之后,就跳回根。

  所以其实(10^18的时候你应该看出来要矩乘了),矩阵中不需要存SAM的每个点,也不能存,只要存现在是什么颜色就好了。

  保证断掉的话,就是f[i][j]表示第i为开头的子串最短多少后面接j就会断。

  这个很好求,先做SAM,求出mn[x][i]表示x这个点后面接最短多少的子串再接j之后就会断。

  mn[x][i]=min(mn[son][i]+1)。

  f[i][j]=mn[1的i儿子][j]。

  然后x次操作就是f[i][j]^x,矩阵“乘法”的运算实际上是求和取min。

  【看代码吧!

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 #define Maxn 100010
  8 #define LL long long
  9 #define INF 0xfffffff
 10 #define inf 1LL<<60
 11
 12 LL n;
 13 int mymin(int x,int y) {return x<y?x:y;}
 14
 15 struct node
 16 {
 17     int pre,son[6],step;
 18 }t[Maxn*2];
 19 bool vis[2*Maxn];
 20
 21 int mn[2*Maxn][6];
 22
 23 struct sam
 24 {
 25     int last,tot;
 26     void extend(int k)
 27     {
 28         int np=++tot,p=last;
 29         t[np].step=t[p].step+1;
 30         while(p&&!t[p].son[k])
 31         {
 32             t[p].son[k]=np;
 33             p=t[p].pre;
 34         }
 35         if(!p) t[np].pre=1;
 36         else
 37         {
 38             int q=t[p].son[k];
 39             if(t[q].step==t[p].step+1) t[np].pre=q;
 40             else
 41             {
 42                 int nq=++tot;
 43                 memcpy(t[nq].son,t[q].son,sizeof(t[nq].son));
 44                 t[nq].step=t[p].step+1;
 45                 t[nq].pre=t[q].pre;
 46                 t[q].pre=t[np].pre=nq;
 47                 while(p&&t[p].son[k]==q)
 48                 {
 49                     t[p].son[k]=nq;
 50                     p=t[p].pre;
 51                 }
 52             }
 53         }
 54         last=np;
 55     }
 56     void dfs(int x)
 57     {
 58         if(vis[x]) return;
 59         vis[x]=1;
 60         for(int i=1;i<=4;i++) mn[x][i]=INF;
 61         for(int i=1;i<=4;i++)
 62         {
 63             if(!t[x].son[i]) mn[x][i]=1;
 64             else
 65             {
 66                 dfs(t[x].son[i]);
 67                 for(int j=1;j<=4;j++) mn[x][j]=mymin(mn[x][j],mn[t[x].son[i]][j]+1);
 68             }
 69         }
 70     }
 71 }sam;
 72
 73 char s[Maxn];
 74
 75 struct Matrix
 76 {
 77     LL w[6][6];
 78     Matrix() {memset(w,0,sizeof(w));}
 79     inline friend Matrix operator * (const Matrix A,const Matrix B)
 80     {
 81         Matrix ret;
 82         for(int i=1;i<=4;i++)
 83          for(int j=1;j<=4;j++)
 84          {
 85             ret.w[i][j]=inf;
 86             for(int k=1;k<=4;k++) ret.w[i][j]=min(ret.w[i][j],A.w[i][k]+B.w[k][j]);
 87          }
 88         return ret;
 89     }
 90     inline friend Matrix operator ^ (const Matrix A,LL k)
 91     {
 92         Matrix ret,tmp=A;
 93         for(int i=1;i<=4;i++) for(int j=1;j<=4;j++) ret.w[i][j]=(i==j)?1:0;
 94         for (;k;k>>=1,tmp=tmp*tmp) if(k&1) ret=ret*tmp;
 95         return ret;
 96     }
 97 }Q;
 98
 99 bool check(LL x)
100 {
101     Matrix B=Q^x;
102     LL mn=inf;
103     for(int i=1;i<=4;i++) for(int j=1;j<=4;j++) mn=min(mn,B.w[i][j]);
104     return mn>=n;
105 }
106
107 int main()
108 {
109     scanf("%lld",&n);
110     scanf("%s",s);
111     int ll=strlen(s);
112     sam.tot=sam.last=1;
113     for(int i=0;i<ll;i++) sam.extend(s[i]-‘A‘+1);
114     memset(vis,0,sizeof(vis));
115     sam.dfs(1);
116
117     for(int i=1;i<=4;i++) for(int j=1;j<=4;j++) Q.w[i][j]=mn[t[1].son[i]][j];
118
119     LL l=1,r=n,ans;
120     while(l<r)
121     {
122         LL mid=(l+r)>>1;
123         if(check(mid)) r=mid;
124         else l=mid+1;
125     }
126     printf("%lld\n",r);
127     return 0;
128 }

看了CA爷的代码,感觉我的矩乘好看多啦!

写在结构体里面很有条理!!

2017-04-17 20:02:16

时间: 2024-10-13 00:55:56

【BZOJ 4180】 4180: 字符串计数 (SAM+二分+矩阵乘法)的相关文章

BZOJ 1009 HNOI2008 GT考试 KMP算法+矩阵乘法

题目大意:给定长度为m的数字串s,求不包含子串s的长度为n的数字串的数量 n<=10^9 光看这个O(n)就是挂 我们不考虑这个 令f[i][j]为长度为i的数字串中最后j位与s中的前j位匹配的方案数 比如当s为12312时 f[i][3]表示长度为i,以123结尾且不包含子串"12312"的方案数 a[x][y]为f[i-1][x]转移至f[i][y]的方案数 换句话说(可能描述不清楚) a[x][y]为s的长度为x的前缀加上一个数字后 后缀可以与最长长度为y的前缀匹配 这个数

BZOJ 1875 SDOI 2009 HH去散步 矩阵乘法优化DP

题目大意:给出一张无向图,求从A到B走k步(不能走回头路)的方案数.(k <= 2^30) 思路:看到k的范围就知道是矩阵乘法了.关键是不能走回头路怎么构造.正常的方法构造点的转移不能避免这个问题,就用边来构造.只要保证不经过自己^1的边就可以保证不走回头路了. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX

BZOJ 1009 [HNOI2008]GT考试 AC自动机+矩阵乘法

题意:链接略 方法: AC自动机+矩阵乘法 解析: 和POJ 2778 一样的题. 大概的思路就是我们建AC自动机的时候需要注意如果某个点是一个串的结尾的话,那么下面的节点都要看成结尾节点. 然后按照AC自动机赋一下矩阵内部值就好了. 赋的矩阵代表从一个节点走一步走到另一个节点有多少方案. 然后经典模型,矩阵的n次方即可. 代码: #include <queue> #include <cstdio> #include <cstring> #include <ios

POJ 3233 Matrix Power Series 二分+矩阵乘法

链接:http://poj.org/problem?id=3233 题意:给一个N*N的矩阵(N<=30),求S = A + A^2 + A^3 + - + A^k(k<=10^9). 思路:非常明显直接用矩阵高速幂暴力求和的方法复杂度O(klogk).肯定会超时.我採用的是二分的方法, A + A^2 + A^3 + - + A^k=(1+A^(k/2)) *(A + A^2 + A^3 + - + A^(k/2)).这样就能够提出一个(1+A^(k/2)),假设k是奇数,单独处理A^k.

【BZOJ】2875: [Noi2012]随机数生成器(矩阵乘法+快速乘)

http://www.lydsy.com/JudgeOnline/problem.php?id=2875 矩阵的话很容易看出来.....我就不写了.太水了. 然后乘法longlong会溢出...那么我们用快速乘...就是将快速幂的乘法变成加法...这种很简单吧.. #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <iostream>

Acdreamoj1116(Gao the string!)字符串hash+二分+矩阵快速幂

Problem Description give you a string, please output the result of the following function mod 1000000007 n is the length of the string f() is the function of fibonacci, f(0) = 0, f(1) = 1... a[i] is the total number of times any prefix appear in the

【BZOJ 2738】 矩阵乘法

2738: 矩阵乘法 Time Limit: 20 Sec Memory Limit: 256 MB Submit: 841 Solved: 351 [Submit][Status][Discuss] Description 给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. Input 第一行两个数N,Q,表示矩阵大小和询问组数: 接下来N行N列一共N*N个数,表示这个矩阵: 再接下来Q行每行5个数描述一个询问:x1,y1,x2,y2,k表示找到以(x1,y1)为左上角.

[BZOJ 1009] [HNOI2008] GT考试 【AC自动机 + 矩阵乘法优化DP】

题目链接:BZOJ - 1009 题目分析 题目要求求出不包含给定字符串的长度为 n 的字符串的数量. 既然这样,应该就是 KMP + DP ,用 f[i][j] 表示长度为 i ,匹配到模式串第 j 位的字符串个数,然后转移就是可以从第 j 位加上一个字符转移到另一个位置. 然而..我并没有写过KMP + DP,我觉得还是写AC自动机+DP比较简单..于是,尽管只有一个模式串,我还是写了AC自动机+DP. 然后就是建出AC自动机,f[i][j] 表示长度为 i ,走到节点 j 的字符串的个数.

【CDQ】BZOJ 2738 矩阵乘法

题意:给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数 思路: 整体二分+二维树状数组 二分询问的答案mid,将数值小等mid的全部插入二维树状数组 然后查询每个矩阵内的元素个数,若数量>K-1则放左边,否则放右边 继续向下分治,左边二分l-mid,右边mid-r 代码: #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include