[洛谷P1420]最长连号

题目大意:输入$n$个正整数,($1\leq n\leq 10000$),要求输出最长的连号的长度。(连号指从小到大连续自然数)

题解:考虑从小到大连续自然数差分为$1$,所以可以把原数列差分(后缀自动机不怎么会写啊),查询串为$1,11,111,\dots,111\dots(n-1个1)$,把它们插入到$AC$自动机中,然后查询即可,原数列差分不为$0,1$时赋为$0$(因为不对答案有影响)

卡点:1.原序列差分的数不为$0,1$时会挂。。。

 

C++ Code:($AC$自动机2018-8-13)

#include <cstdio>
#include <queue>
#define maxn 10010
using namespace std;
int n;
int s[maxn], a[maxn], b[maxn];
int nxt[maxn][2], fail[maxn], cnt[maxn], tot;
int root = 0;
queue<int> q;
void add(int *s, int n) {
	int now = root, len = n;
	for (int i = 1; i <= len; i++) {
		if (nxt[now][s[i]]) now = nxt[now][s[i]];
		else now = nxt[now][s[i]] = ++tot;
		cnt[now]++;
	}
}
void build() {
	for (int i = 0; i < 2; i++) 
		if (nxt[root][i]) fail[nxt[root][i]] = root, q.push(nxt[root][i]);
	while (!q.empty()) {
		int x = q.front(); q.pop();
		for (int i = 0; i < 2; i++) {
			if (nxt[x][i]) fail[nxt[x][i]] = nxt[fail[x]][i], q.push(nxt[x][i]);
			else nxt[x][i] = nxt[fail[x]][i];
		}
	}
}
int ask(int *s, int n) {
	int now = root, ans = 0, len = n;
	for (int i = 1; i <= n; i++) {
		if (s[i] > 1 || s[i] < 0) s[i] = 0;
		now = nxt[now][s[i]];
		for (int j = now; j && ~cnt[j]; j = fail[j]) ans += cnt[j], cnt[j] = -1;
	}
	return ans;
} 
int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", &s[i]), b[i] = 1;
	for (int i = 2; i <= n; i++) a[i - 1] = s[i] - s[i - 1];
	add(b, n - 1);
	build();
	printf("%d\n", ask(a, n - 1) + 1);
	return 0;
}

 

  

题解:当然我们也可以用后缀自动机来做,对差分串建一个后缀自动机,发现若答案长度为$len$,那么长度为$len-1$也是答案,所以可以二分答案(如果你直接求最长的$1$的字段也可以,但我就得这样不是很优美有趣)。

卡点:

 

C++ Code:

#include <cstdio>
#include <cstring>
#define maxn 20010
using namespace std;
int nxt[maxn][2], fail[maxn], R[maxn], idx;
int last, now, np, p, t, root;
int n, a[maxn];
void insert(int x) {
    if (x < 0 || x > 1) x = 0;
    R[now = ++idx] = R[p = last] + 1; last = now;
    for (; ~p && !nxt[p][x]; p = fail[p]) nxt[p][x] = now;
    if (!~p) {fail[now] = root; return ;}
    if (R[t = nxt[p][x]] == R[p] + 1) {fail[now] = t; return ;}
    R[np = ++idx] = R[p] + 1;
    for (int i = 0; i < 2; i++) nxt[np][i] = nxt[t][i];
    fail[np] = fail[t]; fail[t] = fail[now] = np;
    for (; nxt[p][x] == t; p = fail[p]) nxt[p][x] = np;
}
int tmp[maxn];
bool check(int mid) {
    for (int i = 1; i <= mid; i++) tmp[i] = 1;
    int now = root;
    for (int i = 1; i <= mid; i++) {
		now = nxt[now][tmp[i]];
		if (!now) return false;
	}
	return true;
}
int main() {
    scanf("%d", &n);
    fail[root = 0] = -1; idx = last = 1;
    scanf("%d", &a[1]);
    for (int i = 2; i <= n; i++) {
        scanf("%d", &a[i]);
        insert(a[i] - a[i - 1]);
    }
    int l = 0, r = n, ans = 0;
    while (l <= r) {
        int mid = l + r >> 1;
        if (check(mid)) {
            l = mid + 1;
            ans = mid;
        } else r = mid - 1;
    }
    printf("%d\n", ans + 1);
}

  

转载于:https://www.cnblogs.com/Memory-of-winter/p/9470635.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值