↓ 跳过正文
  1. Posts/

codeforces 123 D. String (后缀数组+两次二分得到区间+rmq)

·1380 字·3 分钟

题目链接

题意:定义一个函数 F。

For example: F(babbabbababbab, babb) = 6. The list of pairs is as follows:

(1, 4), (4, 7), (9, 12)

Its continuous sequences are:

  • (1, 4)
  • (4, 7)
  • (9, 12)
  • (1, 4), (4, 7)
  • (4, 7), (9, 12)
  • (1, 4), (4, 7), (9, 12)

二分。

题目描述得很烂,看例子吧,大概就是:如果字符串 y 在字符串 x 中出现 n 次,那么 F(x,y)=n*(n+1)/2

现在给一个字符串,求所有的 F(s,x) 的和,x 为字符串的所有不相同的子串。

思路:由于刚刚写了一个求一个字符串所有不同子串个数的题目,于是就想到了后缀数组。

写完之后观察 height[i]。如果把 height[i] 看成底在 x 轴上的第 i 个矩形的高的话,n 就是一段连续的矩形的长度。

然后暴力会 TLE 48

题解说单调栈,但是单调栈之后还要线段树 or 并查集?(by 羊神)

不会啊 orz

最后用了二分 + rmq 过掉的

大概就是两次二分得到一个矩形的区间,和 whust2016 #1 的那道题有点像。

代码实现
  1/* ***********************************************
  2Author :111qqz
  3Created Time :2016年08月01日 星期一 04时57分01秒
  4File Name :code/cf/problem/123d.cpp
  5************************************************ */
  6#include <cstdio>
  7#include <cstring>
  8#include <iostream>
  9#include <algorithm>
 10#include <vector>
 11#include <queue>
 12#include <set>
 13#include <map>
 14#include <string>
 15#include <cmath>
 16#include <cstdlib>
 17#include <ctime>
 18#include <stack>
 19#define fst first
 20#define sec second
 21#define lson l,m,rt<<1
 22#define rson m+1,r,rt<<1|1
 23#define ms(a,x) memset(a,x,sizeof(a))
 24typedef long long LL;
 25#define pi pair < int ,int >
 26#define MP make_pair
 27using namespace std;
 28const double eps = 1E-8;
 29const int dx4[4]={1,0,0,-1};
 30const int dy4[4]={0,-1,1,0};
 31const int inf = 0x3f3f3f3f;
 32const int N=1E5+7;
 33int n,sa[N],rk[N],t[N],t2[N],cnt[N];
 34int height[N];
 35char s[N];
 36int cmp( int *r , int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
 37void getSa( int n,int m)
 38{
 39    int *x = t;
 40    int *y = t2;
 41    ms(cnt,0);
 42    for ( int i = 0 ; i  < n ; i++) cnt[x[i]=s[i]]++;
 43    for ( int i = 1 ; i < m ; i++) cnt[i] +=cnt[i-1];
 44    for ( int i = n-1 ; i >= 0 ; i--) sa[--cnt[x[i]]] = i ;
 45    for ( int k = 1 ; k <= n ; k <<=1)
 46    {
 47	int p = 0 ;
 48	for ( int i = n-k ; i < n;  i++) y[p++] = i;
 49	for  ( int i = 0 ; i < n ; i++) if (sa[i]>=k) y[p++] = sa[i]-k;
 50	ms(cnt,0);
 51	for ( int i = 0 ; i <n ; i++) cnt[x[y[i]]]++;
 52	for ( int i = 0 ; i < m ; i++) cnt[i] +=cnt[i-1];
 53	for ( int i = n-1 ; i >= 0 ; i--) sa[--cnt[x[y[i]]]] = y[i];
 54	swap(x,y);
 55	p = 1;
 56	x[sa[0]] = 0 ;
 57	for ( int i = 1 ;i  <n ; i++)
 58	    x[sa[i]] = cmp(y,sa[i-1],sa[i],k)?p-1:p++;
 59	if (p>=n) break;
 60	m = p;
 61    }
 62}
 63void getHeight( int n)
 64{
 65    int k = 0 ;
 66    for ( int i = 0 ; i < n ; i++) rk[sa[i]] = i ;
 67    height[0] =  0 ;
 68    for ( int  i = 0 ; i < n ; i++)
 69    {
 70	if (rk[i]==0) continue;
 71	if (k) k--;
 72	int j = sa[rk[i]-1];
 73	while (s[i+k]==s[j+k]) k++;
 74	height[rk[i]] = k;
 75    }
 76}
 77int getSuffix( char s[])
 78{
 79    int len =  strlen(s);
 80    int up = 0 ;
 81    for ( int i = 0 ; i < len ; i++)
 82    {
 83	int  val = s[i];
 84	up = max(up,val);
 85    }
 86    s[len++]='$';
 87    getSa(len,up+1);
 88    getHeight(len);
 89    return len;
 90}
 91LL cal( LL x)
 92{
 93    return x*(x+1)/2LL;
 94}
 95LL solve2(int n)
 96{
 97	 for ( int i = 0 ; i < n ; i++) cout<<i<<" "<<height[i]<<endl;
 98    ms(cnt,0);
 99    int up = 0 ;
100    for ( int i = 1 ; i < n;  i++) cnt[height[i]]++,up=max(up,height[i]);
101    for ( int i = up-1 ; i >= 0 ; i--) cnt[i] = cnt[i]+cnt[i+1];
102    LL res = 0LL ;
103    for ( int i = 0 ; i <= up ; i++)
104	res = res + cal(1LL*cnt[i]);
105    return res;
106}
107/*
108stack<int>stk;
109int l[N],r[N];
110bool visl[N],visr[N];
111
112LL solve ( LL n)  //单调栈
113{
114    LL res = n*(n-1LL)/2LL;
115    int up = 0 ;abaabaabaaba
116    for ( int i = 1 ; i < n; i++) up = max(height[i],up);
117 //   for ( int i = 1 ; i <= n ; i++) cout<<"i:"<<i<<" height[i]:"<<height[i]<<endl;
118    height[0] = -1;
119    height[n+1] = -1 ; //单调队列的哨兵
120    stk.push(0);
121    int x;
122    for ( int i = 1 ; i <= n; i++)
123    {
124	for (x=stk.top();height[x]>=height[i];x=stk.top()) stk.pop();
125	l[i] = x+1;
126	stk.push(i);
127//	cout<<"i:"<<i<<endl;
128    }
129    while (!stk.empty()) stk.pop() ; stk.push(n+1);
130    for ( int i  = n ; i >=1 ; i--)
131    {
132	for (x=stk.top() ; height[x]>=height[i] ; x = stk.top()) stk.pop();
133	r[i] = x-1;
134	stk.push(i);
135    }
136  //  for ( int i = 1 ; i <= n ; i++)
137//	cout<<"i:"<<i<<" l[i]:"<<l[i]<<" r[i]:"<<r[i]<<" height[i]:"<<height[i]<<" "<<(r[i]-l[i]+1)*height[i]<<endl;
138//    int mxh = 0 ;
139    ms(visl,false);
140    ms(visr,false);
141    int mxh = 0 ;
142    int more =1;
143    for ( int i = 1 ;i  <= n ; i++)
144    {
145	if (height[i]==0)
146	{
147	    mxh = 0 ;
148	    continue;
149	}
150	if (visr[r[i]]&&visl[l[i]])
151	{
152	    mxh = 0 ;
153	    continue;
154	}
155	    if (i>1&&height[i-1]<height[i]) more = height[i]-height[i-1];
156	    if (i<n&&height[i+1]<height[i]) more = min(more,height[i]-height[i+1]);
157	    res = res + cal((r[i]-l[i]+1))*(more);
158	    mxh = max(mxh,height[i]);
159	    visr[r[i]] = true;
160	    visl[l[i]] = true;
161//	    cout<<"i:"<<i<<" res:"<<res<<endl;
162
163      }
164
165    return res;
166}  */
167
168int dp[N][30]; // for rmq;
169void rmq_init( int n)
170{
171    for ( int i = 1 ; i <= n ; i++) dp[i][0] = height[i];
172
173    for ( int j = 1 ; (1<<j) <= n ; j++)
174	for ( int i = 1 ; i + (1<<j) - 1 <= n ; i++)
175	    dp[i][j] = min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
176}
177int rmq( int l,int r)
178{
179    if (l>r) swap(l,r);
180    int k = 0 ;
181    while (1<<(k+1)<=r-l+1) k++;
182    int res = min(dp[l][k],dp[r-(1<<k)+1][k]);
183    return res;
184
185}
186
187LL solve( LL len)
188{
189    rmq_init(len);
190    LL ans = len*(len+1)/2;
191
192 //   for ( int i = 1 ; i <= len ; i++) cout<<i<<" :"<<height[i]<<endl;
193    for ( int i = 1 ; i <= len ; i++)
194    {
195	int l = 0 ,r = i ;
196	while (r>l+1)
197	{
198	    int mid = (l+r)/2;
199	    if (rmq(mid,i-1)>=height[i]) r = mid;
200	    else l = mid;
201	}
202	int L = i,R= len+1;
203	while (R>L+1)
204	{
205	    int mid = (L+R) / 2;
206	    if (rmq(i+1,mid)>height[i]) L = mid;
207	    else R = mid;
208	}
209//	cout<<"i:"<<height[i]<<"L:"<<L<<" r:"<<r<<" ans:"<<ans<<endl;
210	ans +=1LL*height[i]*(L-i+1)*(i-r+1);
211    }
212    return ans;
213}
214int main()
215{
216	#ifndef  ONLINE_JUDGE
217	freopen("code/in.txt","r",stdin);
218  #endif
219	scanf("%s",s);
220	int len = getSuffix(s);
221//	cout<<"len:"<<len-1<<endl;
222	LL ans = solve(len-1);
223	printf("%lld\n",ans);
224  #ifndef ONLINE_JUDGE
225  fclose(stdin);
226  #endif
227    return 0;
228}

相关文章

poj 2406 Power Strings (后缀数组||kmp)

·1756 字·4 分钟
poj 2406 题意:给定一个字符串 L,已知这个字符串是由某个字符串 S 重复 R 次而得到的, 求 R 的最大值 思路:论文题。 转载论文中的题解: 做法比较简单,穷举字符串 S 的长度 k,然后判断是否满足。判断的时候, 先看字符串 L 的长度能否被 k 整除,再看 suffix(1) 和 suffix(k+1) 的最长公共前缀是否等于 n-k。在询问最长公共前缀的时候,suffix(1) 是固定的,所以 RMQ 问题没有必要做所有的预处理,只需求出 height 数组中的每一个数到 height[rank[1]] 之间的最小值即可。整个做法的时间复杂度为 O(n)

spoj SUBST1 - New Distinct Substrings(后缀数组)

·599 字·2 分钟
题目连接 题意:求所有不同的子串个数。 思路:后缀数组。和上一道题一样,就是数据范围变成了 5E4…1A 代码实现 1/* *********************************************** 2Author :111qqz 3Created Time :2016年08月02日 星期二 18时32分28秒 4File Name :code/spoj/subst1.cpp 5************************************************ */ 6#include <cstdio> 7#include <cstring> 8#include <iostream> 9#include <algorithm> 10#include <vector> 11#include <queue> 12#include <set> 13#include <map> 14#include <string> 15#include <cmath> 16#include <cstdlib> 17#include <ctime> 18#define fst first 19#define sec second 20#define lson l,m,rt<<1 21#define rson m+1,r,rt<<1|1 22#define ms(a,x) memset(a,x,sizeof(a)) 23typedef long long LL; 24#define pi pair < int ,int > 25#define MP make_pair 26using namespace std; 27const double eps = 1E-8; 28const int dx4[4]={1,0,0,-1}; 29const int dy4[4]={0,-1,1,0}; 30const int inf = 0x3f3f3f3f; 31const int N=5E4+7; 32char s[N]; 33int sa[N],rk[N],t[N],t2[N],cnt[N],height[N]; 34int cmp (int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l];} 35void getSa(int n,int m) 36{ 37 int *x=t,*y=t2; 38 ms(cnt,0); 39 for ( int i = 0 ; i < n ; i++) cnt[x[i]=s[i]]++; 40 for ( int i = 0 ; i < m ; i++) cnt[i]+=cnt[i-1]; 41 for ( int i = n-1 ; i >= 0 ; i--) sa[--cnt[x[i]]] = i; 42 for ( int k = 1 ; k <= n ; k<<=1) 43 { 44 int p = 0; 45 for ( int i = n-k ; i < n ; i++) y[p++] = i; 46 for ( int i = 0 ; i < n; i++) if (sa[i]>=k) y[p++] = sa[i]-k; 47 ms(cnt,0); 48 for ( int i = 0 ; i <n ; i++) cnt[x[y[i]]]++; 49 for ( int i = 0 ;i < m ; i++) cnt[i]+=cnt[i-1]; 50 for ( int i = n-1 ; i >= 0 ; i--) sa[--cnt[x[y[i]]]] = y[i]; 51 swap(x,y); 52 p = 1; 53 x[sa[0]] = 0; 54 for ( int i = 1 ; i < n ; i++ ) 55 x[sa[i]] = cmp(y,sa[i-1],sa[i],k)?p-1:p++; 56 if (p>=n) break; 57 m = p; 58 } 59} 60void getHeight( int n) 61{ 62 int k = 0 ; 63 for ( int i = 0 ; i <n ; i ++) rk[sa[i]] = i ; 64 height[0] = 0 ; 65 for ( int i = 0 ; i < n ; i++) 66 { 67 if (rk[i]==0) continue; 68 if (k) k--; 69 int j = sa[rk[i]-1]; 70 while (s[i+k]==s[j+k]) k++; 71 height[rk[i]] = k; 72 } 73} 74int getSuffix(char s[]) 75{ 76 int len = strlen(s); 77 int up = 0 ; 78 for ( int i = 0 ; i < len ; i++) 79 { 80 int val = s[i]; 81 up = max(up,val); 82 } 83 s[len++]='$'; 84 getSa(len,up+1); 85 getHeight(len); 86 return len; 87} 88int solve ( int n) 89{ 90 ms(cnt,0); 91 int up = 0; 92 for ( int i = 0 ;i <n ; i ++) cnt[height[i]]++, up = max(up,height[i]); 93 for ( int i = up-1 ; i >= 1; i--) cnt[i]+=cnt[i+1]; 94 int res = 0 ; 95 for ( int i = 1 ; i <=up ; i++) 96 res+=cnt[i]; 97 return res; 98} 99int main() 100{ 101 #ifndef ONLINE_JUDGE 102 freopen("code/in.txt","r",stdin); 103 #endif 104 int T; 105 scanf("%d",&T); 106 while (T--) 107 { 108 scanf("%s",s); 109 int len = getSuffix(s); 110 LL ans = 1LL*len*(len-1)/2; 111 ans-=1LL*solve(len); 112 printf("%lld\n",ans); 113 } 114 #ifndef ONLINE_JUDGE 115 fclose(stdin); 116 #endif 117 return 0; 118}

spoj DISUBSTR - Distinct Substrings (统计字串个数,后缀数组)

·1022 字·3 分钟
题目链接 题意:给出一个字符串,问所有不同的字串的个数。 思路:直接求比较困难。我们考虑,假如组成字符串的所有字符都不相同,那么就没有相同的字串。假设字符串的长度为 n,那么长度为 1 的子串有 n 个,为 2 的有 n-1 个……为 n 的有 1 个,一共就是 n*(n+1)/2 个,但是实际上会有重复的。

poj 3261 Milk Patterns (最长公共子串,后缀数组)

·813 字·2 分钟
poj3261 题意:给一个字符串,要求找出至少出现k次的最长重复子串… 思路:后缀数组,然后再次用到了根据height数组对后缀进行分组的套路…二分判定合法性,对于当前的最长长度x,分组使得每组中的height[i]都大于等于x,所不同的是,判定变成了存在一个组,后缀的个数至少为k个(因为这样,就可以对于大于等于k个的后缀,同时取前x长度,得到的就是出现了至少k次且长度为x的前缀)1A,蛤蛤蛤