题目:

洛谷1117

分析:

定义把我校某兔姓神犇Tzz和他的妹子拆分,为“优秀的拆分”

随便写个哈希就能有\(95\)分的好成绩……

我的\(95\)分做法比fei较chang奇葩,不想浪费时间的可以忽略解法一qwq

解法一:

用\(n\)个vector记录对于每个点\(i\),哪些长度\(len\)满足\(i+2len\leq n\)且\(str[i, i+len)=str[i+len,i+2len)\)(即形如“\(AA\)”)。然后,枚举开头\(l\)和“\(AA\)”的长度\(len\),这种情况下的的答案就是以\(l+2len\)开头的“\(AA\)”数量(即“\(BB\)”)。

解法二:

这题哈希可以过的说qwq(虽然我惨遭卡常没过去)

预处理出以每个点开始和结尾的“\(AA\)”的数量\(st[i]\)和\(ed[i]\),那么答案就是\(\sum\limits_{i=1}^{len-1}ed[i-1]st[i]\)。

暴力枚举一个\(A\)的长度\(len\),然后每隔\(len\)个点作一个标记,共\(\lceil \frac {n}{len} \rceil\)个。可以发现任意长为\(2len\)的“\(AA\)”都会经过两个标记。枚举标记,计算相邻两标记\(a\)和\(b\)开头的后缀的\(LCP\)(最长公共前缀)和两标记结尾的前缀的\(LCS\)(最长公共后缀)。如果\(LCP+LCS\geq len\)则存在一个“\(AA\)”串经过这两个标记,且这些串的起始位置是连续的。简单画个图就可以发现,它们的起始位置是\([a-LCS+1,a+LCP-len]\)。(脑补一下,第一个\(A\)的开头不能比\(a-LCS+1\)小,第一个\(A\)的结尾不能比\(a+LCP-1\)大。)用差分解决区间修改。

求\(LCP\)和\(LCS\)可以二分+哈希解决。根据某些神奇的原理,\(O(\sum\limits _{i=1}^n \frac{n}{i})=O(n\log n)\)(貌似叫调和级数)。里面再套个二分,复杂度\(O(n\log^2n)\)。

解法三:

用后缀数组+ST表\(O(1)\)查询\(LCP\)和\(LCS\),复杂度\(O(n\log n)\)。(什么,你不会后缀数组/不会用后缀数组查\(LCP\)和\(LCS\)?戳我:【知识总结】后缀数组(Suffix_Array)

代码:

一、奇葩的哈希\(95\)分做法

五个月前写的代码,比较奇葩……

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
namespace zyt
{
typedef long long ll;
typedef pair<ll, ll> pll;
const int N = 2010, seed = 131, p[2] = {(int)1e9 + 7, (int)1e9 + 9};
ll F[N][2];
ll h[N][2];
void init()
{
F[0][0] = F[0][1] = 1;
for (int i = 1; i < N; i++)
{
F[i][0] = F[i - 1][0] * seed % p[0];
F[i][1] = F[i - 1][1] * seed % p[1];
}
}
void init(const string &str)
{
h[0][0] = h[0][1] = str[0] - 'a';
for (int t = 0; t < 2; t++)
for (int i = 1; i < str.size(); i++)
h[i][t] = (h[i - 1][t] * seed + str[i] - 'a') % p[t];
}
pll Hash(const string &str)
{
ll ans[2] = {0, 0};
for (int t = 0; t < 2; t++)
for (int i = 0; i < str.size(); i++)
ans[t] = (ans[t] * seed + str[i] - 'a') % p[t];
return make_pair(ans[0], ans[1]);
}
pll Hash(const int l, const int r)
{
if (l == 0)
return make_pair(h[r][0], h[r][1]);
else
{
int len = r - l + 1;
return make_pair(
(h[r][0] - h[l - 1][0] * F[len][0] % p[0] + p[0]) % p[0],
(h[r][1] - h[l - 1][1] * F[len][1] % p[1] + p[1]) % p[1]);
}
}
vector<int>repeat[N];
void work()
{
ios::sync_with_stdio(false);
int T;
init();
cin >> T;
while (T--)
{
int ans = 0;
string str;
cin >> str;
init(str);
for (int l = 0; l < str.size(); l++)
{
vector<int>().swap(repeat[l]);
for (int r = l; r < str.size(); r++)
{
int len = r - l + 1;
if (r + len < str.size() && Hash(l, r) == Hash(r + 1, r + len))
repeat[l].push_back(len * 2);
}
}
for (int l = 0; l < str.size(); l++)
for (int i = 0; i < repeat[l].size(); i++)
{
int r = l + repeat[l][i];
if (r < str.size())
ans += repeat[r].size();
}
cout << ans << endl;
}
}
}
int main()
{
zyt::work();
return 0;
}

二、\(O(n\log^2 n)\)的哈希期望\(100\)实际\(95\)的做法

(我是哪里写挂了还是常数太大啊qwq,\(O(n\log ^2n)\)凭什么过不了\(30000\)啊,我周围一圈神仙都能过的qwq

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
namespace zyt
{
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int N = 3e4 + 10;
namespace Hash
{
typedef pii hash_t;
const hash_t seed = pii(29, 29), p = pii(1e9 + 7, 1e9 + 9);
pii operator + (const pii &a, const pii &b)
{
return make_pair(a.first + b.first, a.second + b.second);
}
pii operator - (const pii &a, const pii &b)
{
return make_pair(a.first - b.first, a.second - b.second);
}
pll operator * (const pii &a, const pii &b)
{
return make_pair((ll)a.first * b.first, (ll)a.second * b.second);
}
pii operator % (const pll &a, const pii &p)
{
return make_pair(a.first % p.first, a.second % p.second);
}
hash_t h[N], pow[N];
inline void init()
{
pow[0] = make_pair(1, 1);
for (int i = 1; i < N; i++)
pow[i] = pow[i - 1] * seed % p;
}
inline int ctoi(const char c)
{
return c - 'a';
}
inline hash_t ctoh(const char c)
{
return make_pair(ctoi(c), ctoi(c));
}
inline void get_hash(const string &s)
{
h[0] = ctoh(s[0]);
for (int i = 1; i < s.size(); i++)
h[i] = (h[i - 1] * seed % p + ctoh(s[i])) % p;
}
inline hash_t extract(const int l, const int r)
{
if (l == 0)
return h[r];
else
return (h[r] - h[l - 1] * pow[r - l + 1] % p + p) % p;
}
}
int st[N], ed[N];
inline int lcp(const int len, const int a, const int b)
{
using Hash::extract;
int l = 1, r = len, ans = 0;
while (l <= r)
{
int mid = (l + r) >> 1;
if (extract(a, a + mid - 1) == extract(b, b + mid - 1))
l = mid + 1, ans = mid;
else
r = mid - 1;
}
return ans;
}
inline int lcs(const int len, const int a, const int b)
{
using Hash::extract;
int l = 1, r = len, ans = 0;
while (l <= r)
{
int mid = (l + r) >> 1;
if (extract(a - mid + 1, a) == extract(b - mid + 1, b))
l = mid + 1, ans = mid;
else
r = mid - 1;
}
return ans;
}
int work()
{
ios::sync_with_stdio(false);
int T;
Hash::init();
cin >> T;
while (T--)
{
string str;
cin >> str;
memset(st, 0, sizeof(int[str.size()]));
memset(ed, 0, sizeof(int[str.size()]));
Hash::get_hash(str);
for (int i = 1; i < str.size(); i++)
for (int j = 0; j + i < str.size(); j += i)
{
int nxt = j + i;
int pre = lcp(min(i, (int)str.size() - nxt + 1), j, nxt);
int suf = lcs(min(i, j + 1), j, nxt);
int sta = min(j - suf + 1, (int)str.size() - (i << 1));
int end = min(j + pre - i, (int)str.size() - (i << 1));
if (pre + suf - 1 >= i)
{
++st[sta], --st[end + 1];
++ed[sta + (i << 1) - 1], --ed[end + (i << 1)];
}
}
for (int i = 1; i < str.size(); i++)
st[i] += st[i - 1], ed[i] += ed[i - 1];
ll ans = 0;
for (int i = 1; i < str.size(); i++)
ans += (ll)ed[i - 1] * st[i];
cout << ans << '\n';
}
return 0;
}
}
int main()
{
return zyt::work();
}

三、\(O(n\log n)\)的后缀数组优秀做法

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
namespace zyt
{
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int N = 3e4 + 10, B = 15, CH = 26;
struct ST
{
int st[N][B];
const int *arr;
static int lg2[N];
static bool lg2_built;
int min(const int a, const int b)
{
return arr[a] < arr[b] ? a : b;
}
void build(const int n, const int *_arr)
{
arr = _arr;
if (!lg2_built)
{
int tmp = 0;
for (int i = 0; i < N; i++)
{
lg2[i] = tmp;
if (i == (1 << (tmp + 1)))
++tmp;
}
lg2_built = true;
}
for (int i = n - 1; i >= 0; i--)
{
st[i][0] = i;
for (int j = 1; j <= lg2[n]; j++)
if (i + (1 << j) - 1 < n)
st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
else
break;
}
}
inline int query(const int l, const int r)
{
int len = lg2[r - l + 1];
return min(st[l][len], st[r - (1 << len) + 1][len]);
}
};
int ST::lg2[N];
bool ST::lg2_built;
struct Suffix_Array
{
int sa[N], rank[N], tp[N], count[N], height[N], kind, len;
ST st;
inline int min(const int a, const int b)
{
return height[a] < height[b] ? a : b;
}
void radix_sort()
{
static int count[N];
memset(count, 0, sizeof(int[kind]));
for (int i = 0; i < len; i++)
++count[rank[tp[i]]];
for (int i = 1; i < kind; i++)
count[i] += count[i - 1];
for (int i = len - 1; i >= 0; i--)
sa[--count[rank[tp[i]]]] = tp[i];
}
void build(const string &s)
{
len = s.size();
for (int i = 0; i < len; i++)
rank[i] = s[i] - 'a', tp[i] = i;
kind = CH;
radix_sort();
for (int tmp = 1; tmp < len; tmp <<= 1)
{
int cnt = 0;
for (int i = len - tmp; i < len; i++)
tp[cnt++] = i;
for (int i = 0; i < len; i++)
if (sa[i] >= tmp)
tp[cnt++] = sa[i] - tmp;
radix_sort();
swap(rank, tp);
rank[sa[0]] = 0;
kind = 1;
for (int i = 1; i < len; i++)
if (tp[sa[i]] == tp[sa[i - 1]] &&
(sa[i] + tmp < len && sa[i - 1] + tmp < len) &&
(tp[sa[i] + tmp] == tp[sa[i - 1] + tmp]))
rank[sa[i]] = rank[sa[i - 1]];
else
rank[sa[i]] = kind++;
if (kind == len)
break;
}
int k = 0;
for (int i = 0; i < len; i++)
{
if (!rank[i])
continue;
if (k)
--k;
int j = sa[rank[i] - 1];
while (i + k < len && j + k < len && s[i + k] == s[j + k])
++k;
height[rank[i]] = k;
}
st.build(len, height);
}
}sa1, sa2;
int st[N], ed[N];
inline int lcp(const int a, const int b)
{
int ra = sa1.rank[a], rb = sa1.rank[b];
return sa1.height[sa1.st.query(min(ra, rb) + 1, max(ra, rb))];
}
inline int lcs(const int len, const int a, const int b)
{
int ra = sa2.rank[len - a - 1], rb = sa2.rank[len - b - 1];
return sa2.height[sa2.st.query(min(ra, rb) + 1, max(ra, rb))];
}
int work()
{
ios::sync_with_stdio(false);
int T;
cin >> T;
while (T--)
{
string str, rev;
cin >> str;
memset(st, 0, sizeof(int[str.size()]));
memset(ed, 0, sizeof(int[str.size()]));
sa1.build(str);
rev = str, reverse(rev.begin(), rev.end());
sa2.build(rev);
for (int i = 1; i < str.size(); i++)
for (int j = 0; j + i < str.size(); j += i)
{
int nxt = j + i;
int pre = min(i, lcp(j, nxt));
int suf = min(i, lcs(str.size(), j, nxt));
int sta = min(j - suf + 1, (int)str.size() - (i << 1));
int end = min(j + pre - i, (int)str.size() - (i << 1));
if (pre + suf - 1 >= i)
{
++st[sta], --st[end + 1];
++ed[sta + (i << 1) - 1], --ed[end + (i << 1)];
}
}
for (int i = 1; i < str.size(); i++)
st[i] += st[i - 1], ed[i] += ed[i - 1];
ll ans = 0;
for (int i = 1; i < str.size(); i++)
ans += (ll)ed[i - 1] * st[i];
cout << ans << '\n';
}
return 0;
}
}
int main()
{
return zyt::work();
}

【洛谷1117_BZOJ4650】[NOI2016] 优秀的拆分(哈希_后缀数组_RMQ)的更多相关文章

  1. bzoj 4650(洛谷 1117) [Noi2016]优秀的拆分——枚举长度的关键点+后缀数组

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4650 https://www.luogu.org/problemnew/show/P1117 ...

  2. 【洛谷4770/UOJ395】[NOI2018]你的名字(后缀数组_线段树合并)

    题目: 洛谷4770 UOJ395 分析: 一个很好的SAM应用题-- 一句话题意:给定一个字符串\(S\).每次询问给定字符串\(T\)和两个整数\(l\).\(r\),求\(T\)有多少个本质不同 ...

  3. 洛谷P2336 [SCOI2012]喵星球上的点名(后缀数组+莫队)

    我学AC自动机的时候就看到了这题,想用AC自动机结果被学长码风劝退-- 学后缀数组时又看到了这题--那就写写后缀数组做法吧 结果码风貌似比当年劝退我的学长还毒瘤啊 对所有的模式串+询问串,不同串之间用 ...

  4. 【BZOJ4560】[NOI2016]优秀的拆分

    [BZOJ4560][NOI2016]优秀的拆分 题面 bzoj 洛谷 题解 考虑一个形如\(AABB\)的串是由两个形如\(AA\)的串拼起来的 那么我们设 \(f[i]\):以位置\(i\)为结尾 ...

  5. [NOI2016]优秀的拆分&&BZOJ2119股市的预测

    [NOI2016]优秀的拆分 https://www.lydsy.com/JudgeOnline/problem.php?id=4650 题解 如果我们能够统计出一个数组a,一个数组b,a[i]表示以 ...

  6. luogu1117 [NOI2016]优秀的拆分

    luogu1117 [NOI2016]优秀的拆分 https://www.luogu.org/problemnew/show/P1117 后缀数组我忘了. 此题哈希可解决95分(= =) 设\(l_i ...

  7. [UOJ#219][BZOJ4650][Noi2016]优秀的拆分

    [UOJ#219][BZOJ4650][Noi2016]优秀的拆分 试题描述 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 A 和 B 是任意非空字符串,则我们称该字符串的这种拆分是优秀 ...

  8. [NOI2016]优秀的拆分(SA数组)

    [NOI2016]优秀的拆分 题目描述 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 \(aabaaba ...

  9. 洛谷P1712 [NOI2016]区间 尺取法+线段树+离散化

    洛谷P1712 [NOI2016]区间 noi2016第一题(大概是签到题吧,可我还是不会) 链接在这里 题面可以看链接: 先看题意 这么大的l,r,先来个离散化 很容易,我们可以想到一个结论 假设一 ...

随机推荐

  1. for 循环新的写法==列表解析

    1. (for x in L1) 是一个可迭代对象: 2. 列表解析比for 循环快,列表解析的迭代在解释器内部是以C语言速度执行, 而不是手动python代码执行: (x+10 for x in L ...

  2. 03 Python的那些事

    目录: 1) 创始人以及重要发展历程 2) Python语言的特点 3) TIOBE排名 4) 解释器 5) Python后缀名 6) 变量规则和约定 7) 常量 8) 注释 9) 缩进 10) Py ...

  3. 突击战 (UVA 11729)

    题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=28436 思路:任务从开始时就不停执行,与其他任务毫无关联,当然是执 ...

  4. 用记事本写第一个Java程序

    public class Welcome{ public static void main(String[] args){ System.out.println("我是尚学堂的高淇,很高兴认 ...

  5. JavaSE 学习笔记之包装类(十七)

    基本数据类型对象包装类:是按照面向对象思想将基本数据类型封装成了对象. 好处: 1:可以通过对象中的属性和行为操作基本数据. 2:可以实现基本数据类型和字符串之间的转换. 关键字   对应的类名 by ...

  6. 常州模拟赛d7t1 亲戚

    分析:把题目换个方式理解,就是把各个点排成一列,并且指定了若干对的先后次序,问你有多少种序列满足要求. 显然是一道dp题,直接推出方程似乎有点点困难,那么先看看数据特点. 1.有一些点满足fi=0,那 ...

  7. hdu 2094拓扑排序map实现记录

    #include<stdio.h> #include<iostream> #include<algorithm> #include<string> #i ...

  8. 总结懒加载的解决方法(全)org.hibernate.LazyInitializationException: could not initialize proxy - no Session

    如下错误:org.hibernate.LazyInitializationException: could not initialize proxy - no Session 原因是懒加载的问题,因为 ...

  9. nyoj_116_士兵杀敌(二)_201404131107

    士兵杀敌(二) 时间限制:1000 ms  |  内存限制:65535 KB 难度:5   描述 南将军手下有N个士兵,分别编号1到N,这些士兵的杀敌数都是已知的. 小工是南将军手下的军师,南将军经常 ...

  10. zoj——1202 Divide and Count

    Divide and Count Time Limit: 2 Seconds      Memory Limit: 65536 KB Jack has several beautiful diamon ...