zoukankan      html  css  js  c++  java
  • HDU6988 Display Substring

    传送门


    这就是一个板儿题,结果好几种智障操作白给了。


    首先用一种后缀数据结构求出本质不同的子串个数,再二分答案。

    (我比赛的时候想复杂了,将第(k)小转换成了第(k)大,但本质上是一样的)

    对于当前二分值(mid),我们要找所有权值大于等于(mid)的子串个数,那么对于SAM上的每一个点,可以要么选这个节点代表的所有子串,要么选择一部分子串。那么再进行一遍二分就行了。

    时间复杂度(O(nlog^2n)).

    #include<cstdio> 
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<assert.h>
    using namespace std;
    #define space putchar(' ')
    #define enter puts("")
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define forE(i, x, y) for(int i = head[x], y; ~i && ((y = e[i].to) >= 0); i = e[i].nxt)
    #define In 
    typedef long long ll;
    const int INF = 0x3f3f3f3f;
    const int maxs = 27;
    const int maxn = 8e5 + 5;
    In ll read()
    {
    	ll ans = 0;
    	char ch = getchar(), las = ' ';
    	while(!isdigit(ch)) las = ch, ch = getchar();
    	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    	if(las == '-') ans = -ans;
    	return ans;
    }
    In void write(ll x)
    {
    	if(x < 0) putchar('-'), x = -x;
    	if(x >= 10) write(x / 10);
    	putchar(x % 10 + '0');
    }
    
    char s[maxn];
    int n, c[maxs], sum[maxn];
    ll K;
    
    struct Sam
    {
    	int tra[maxn << 1][maxs], link[maxn << 1], len[maxn << 1], endp[maxn << 1], cnt, las;
    	In void init() {link[cnt = las = 0] = -1; Mem(tra[0], 0);}
    	In void insert(int c, int id)
    	{
    		int now = ++cnt, p = las; Mem(tra[now], 0);
    		len[now] = len[p] + 1, endp[now] = id;
    		while(~p && !tra[p][c]) tra[p][c] = now, p = link[p];
    		if(p == -1) link[now] = 0;
    		else
    		{
    			int q = tra[p][c];
    			if(len[q] == len[p] + 1) link[now] = q;
    			else
    			{
    				int clo = ++cnt;
    				memcpy(tra[clo], tra[q], sizeof(tra[q]));
    				len[clo] = len[p] + 1, endp[clo] = endp[q];
    				link[clo] = link[q]; link[q] = link[now] = clo;
    				while(~p && tra[p][c] == q) tra[p][c] = clo, p = link[p];
    			}
    		}
    		las = now;
    	}
    	int buc[maxn << 1], pos[maxn << 1];
    	In ll solve()
    	{
    		ll ret = 0;
    		for(int i = 1; i <= cnt; ++i) ret += len[i] - len[link[i]];
    		return ret;
    	}
    	In int ggSum(int L, int R)
    	{
    		if(L == 0) return sum[R];
    		else return sum[R] - sum[L - 1];
    	}
    	In int gSum(int now) 
    	{
    		return ggSum(endp[now] - len[now] + 1, endp[now]);
    	}
    	In ll calc(int x)
    	{
    		ll ret = 0;
    		for(int i = 1; i <= cnt; ++i)
    		{
    			if(gSum(i) >= x)
    			{
    				ll L = len[link[i]] + 1, R = len[i];
    				while(L < R)
    				{
    					int mid = (L + R) >> 1;
    					if(ggSum(endp[i] - mid + 1, endp[i]) >= x) R = mid;
    					else L = mid + 1;
    				}			
    				ret += len[i] - L + 1;
    			}			
    		}
    		return ret;
    	}
    }S;
    
    In int solve()
    {
    	S.init();
    	for(int i = 0; i < n; ++i) S.insert(s[i] - 'a', i);
    	for(int i = 0; i < n; ++i)
    	{
    		if(i) sum[i] = sum[i - 1] + c[s[i] - 'a'];
    		else sum[i] = c[s[i] - 'a'];
    	}
    	ll Sum = S.solve();
    	K = Sum - K + 1;
    	if(K < 1) return -1;
    	int L = 0, R = sum[n - 1];
    	while(L < R)
    	{
    		int mid = (L + R + 1) >> 1;
    		if(S.calc(mid) >= K) L = mid;
    		else R = mid - 1;
    	}
    	return L;
    }
    
    int main()
    {
    	int T = read();
    	while(T--)
    	{
    		n = read(), K = read();
    		scanf("%s", s);
    		for(int i = 0; i < 26; ++i) c[i] = read();
    		write(solve()), enter;
    	}
    	return 0;
    }
    

    比赛的时候还建出了后缀树,然后进行树上差分,麻烦的很。

  • 相关阅读:
    【oracle ocp知识点一】
    poj 3280 区间dp
    TextView划线 android
    Apple Watch 集成环信SDK
    Android中使用HttpClient实现HTTP通信效果
    js 数组操作大集合
    5.5二叉树的遍历
    oracle学习之路(二)------数组类型/记录类型的使用
    推荐系统个人理解(实践部分)
    openstack之虚拟机创建流程分析
  • 原文地址:https://www.cnblogs.com/mrclr/p/15076967.html
Copyright © 2011-2022 走看看