• 【洛谷1117_BZOJ4650】[NOI2016] 优秀的拆分(哈希_后缀数组_RMQ)


    题目:

    洛谷1117

    分析:

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

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

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

    解法一:

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

    解法二:

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

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

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

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

    解法三:

    用后缀数组+ST表(O(1))查询(LCP)(LCS),复杂度(O(nlog 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(nlog^2 n))的哈希期望(100)实际(95)的做法

    (我是哪里写挂了还是常数太大啊qwq,(O(nlog ^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 << '
    ';
    		}
    		return 0;
    	}
    }
    int main()
    {
    	return zyt::work();
    }
    

    三、(O(nlog 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 << '
    ';
    		}
    		return 0;
    	}
    }
    int main()
    {
    	return zyt::work();
    }
    
  • 相关阅读:
    Java 猜字谜游戏
    Java中private、protected、public和default的区别
    Java JRT
    Java 数组
    javascript中的bind()方法
    2018-07-31 javascript中对的apply,call
    2018年7月31日
    【转载】javascript回调函数
    函数return以及lodash中的extend,each,map方法
    css实现动态相对居中
  • 原文地址:https://www.cnblogs.com/zyt1253679098/p/10129625.html
Copyright © 2020-2023  润新知