【HDU5745 2016 Multi-University Training Contest 2L】【bitset做法or暴力】La Vie en rose 目标串多少子串可以被原始串做相邻交换得到

55 篇文章 0 订阅
5 篇文章 0 订阅

La Vie en rose

Time Limit: 7000/3500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1938    Accepted Submission(s): 655


Problem Description
Professor Zhang would like to solve the multiple pattern matching problem, but he only has only one pattern string   p=p1p2...pm . So, he wants to generate as many as possible pattern strings from   p  using the following method:

1. select some indices   i1,i2,...,ik  such that   1i1<i2<...<ik<|p|  and   |ijij+1|>1  for all   1j<k .
2. swap   pij  and   pij+1  for all   1jk .

Now, for a given a string   s=s1s2...sn , Professor Zhang wants to find all occurrences of all the generated patterns in   s .
 

Input
There are multiple test cases. The first line of input contains an integer   T , indicating the number of test cases. For each test case:

The first line contains two integers   n  and   m   (1n105,1mmin{5000,n})  -- the length of   s  and   p .

The second line contains the string   s  and the third line contains the string   p . Both the strings consist of only lowercase English letters.
 

Output
For each test case, output a binary string of length   n . The   i -th character is "1" if and only if the substring   sisi+1...si+m1  is one of the generated patterns.
 

Sample Input
  
  
3 4 1 abac a 4 2 aaaa aa 9 3 abcbacacb abc
 

Sample Output
  
  
1010 1110 100100100
 

Author
zimpha
 

Source
【HDU5745 2016 Multi-University Training Contest 2L】【暴力做法】La Vie en rose 目标串多少子串可以被原始串做相邻交换得到

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 1e5+10, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n, m;
char a[N], b[N], ans[N];
bool check(int st)
{
	for (int j = 0; j < m; ++j)
	{
		if (a[st + j] != b[j])
		{
			if (a[st + j + 1] == b[j] && a[st + j] == b[j + 1])++j;
			else return 0;
		}
	}
	return 1;
}
int main()
{
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d%d", &n, &m);
		scanf("%s%s", a + 1, b);
		for (int i = 1; i <= n; ++i)ans[i] = '0';
		ans[n + 1] = 0;
		for (int i = 1; i + m - 1 <= n; ++i)
		{
			if (check(i))ans[i] = '1';
		}
		puts(ans + 1);
	}
	return 0;
}
/*
【trick&&吐槽】
这题竟然暴力就够AC了,惊了个呆!

【题意】
给你一个长度为n(1e5)的目标字符串。
我们有一个长度为m(min(5000,n))的基础串,
对于这个目标字符串,显然有n-m+1个长度为m的子串
问你,在这些子串中,有哪些子串,是可以通过对基础串一些相邻位置的交换,得到的。

即,我们可以选出若干个基础串的若干个位置p[],使得p[i+1]>p[i]+1,
然后我们swap(p[i],p[i]+1),就可以实现变形后的基础串与目标字符串的匹配

【类型】
暴力 or bitset

【分析】
正解是bitset——

【时间复杂度&&优化】
O(nm/32)

*/

【HDU5745 2016 Multi-University Training Contest 2L】【bitset做法】La Vie en rose 目标串多少子串可以被原始串做相邻交换得到

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 1e5 + 10, M = 5000, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n, m;
char a[N], b[N], ans[N];
bitset<M>D[26];//D[i]表示字符i在b中出现的位置
bitset<M>L[26];//L[i]表示字符i在b中出现的位置(所有位置都右移一位)
bitset<M>R[26];//R[i]表示字符i在b中出现的位置(所有位置都左移一位)
bitset<M>dp[3];
bitset<M>bef,beh;
int main()
{
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d%d", &n, &m);
		scanf("%s%s", a, b);
		for (int i = 1; i <= n; ++i)ans[i] = '0';
		for (int i = 0; i < 26; ++i)D[i].reset(), L[i].reset(), R[i].reset();
		for (int i = 0; i < m; ++i) 
		{
			int x = b[i] - 'a';
			D[x][i] = 1;
			if(i<4999)L[x][i + 1] = 1;
			if(i)R[x][i - 1] = 1;
		}
		for (int i = 0; i < n; ++i) ans[i] = '0';
		dp[0].reset(); 
		dp[1].reset(); 
		dp[2].reset();

		/*
		dp[i][j][0]=dp[i-1][j-1][2] & (a[i]==b[j-1])
		//a[i-1]与b[0~j-1]匹配了,且a[i]实际要与b[j-1]做匹配

		dp[i][j][1]=dp[i-1][j-1][0] & (a[i]==b[j])
				   |dp[i-1][j-1][1] & (a[i]==b[j])
		//a[i-1]与b[0~j-1]匹配了,且a[i]实际要与b[j]做匹配

		dp[i][j][2]=dp[i-1][j-1][0] & (a[i]==b[j+1])
				   |dp[i-1][j-1][1] & (a[i]==b[j+1])
		//a[i-1]与b[0~j-1]匹配了,且a[i]实际要与b[j+1]做匹配

		这个DP是有for(i)for(j)的
		我们只是把这个DP的for(i)用层层展开滚动数组的形式实现,用bitset来实现for(j)的整体展开
		即,对于位点i,dp这个bitset的j位,如果为1,代表以a[]的位点i为末尾,可以匹配到b[]的位点j
		*/
		for (int i = 0; i < n; ++i) 
		{
			int x = a[i] - 'a';
			bef = (dp[2] << 1);					//dp[j-1][2]
			beh = (dp[1] << 1) | (dp[0] << 1);	//dp[j-1][0] | dp[j-1][1]
			bef[0] = beh[0] = 1;
			dp[0] = bef & L[x];					//dp[0]的意义是,对于a[]的i位点,在匹配b[]的时候,实际是匹配b[]的j-1位点
			dp[1] = beh & D[x];					//dp[1]的意义是,对于a[]的i位点,在匹配b[]的时候,实际是匹配b[]的j位点
			dp[2] = beh & R[x];					//dp[2]的意义是,对于a[]的i位点,在匹配b[]的时候,实际是匹配b[]的j+1位点
			if (dp[1][m - 1] || dp[0][m - 1]) ans[i - m + 1] = '1';
		}
		ans[n] = 0;
		puts(ans);
	}
	return 0;
}
/*
【trick&&吐槽】
这题竟然暴力就够AC了,惊了个呆!

【题意】
给你一个长度为n(1e5)的目标字符串a
我们有一个长度为m(min(5000,n))的基础串b,
对于这个目标字符串,显然有n-m+1个长度为m的子串
问你,在这些子串中,有哪些子串,是可以通过对基础串一些相邻位置的交换,得到的。

即,我们可以选出若干个基础串的若干个位置p[],使得p[i+1]>p[i]+1,
然后我们swap(p[i],p[i]+1),就可以实现变形后的基础串与目标字符串的匹配

【类型】
暴力 or bitset

【分析】
尽管暴力可过,但是正解是bitset,我们依然需要学习一下——
这题的暴力做法,其实本身就比较相似于一个DP。

这道题定义f[i][j][k]表示——
a串匹配到位点a[i]
b串匹配到位点b[j]
j这个位置的匹配状态为k(0表示b[j]要与b[j-1]交换,1表示没有交换,2表示b[j+1]交换)

转移方程是这个样子的——
dp[i][j][0]=dp[i-1][j-1][2] & (a[i]==b[j-1])
//a[i-1]与b[0~j-1]匹配了,且a[i]实际要与b[j-1]做匹配

dp[i][j][1]=dp[i-1][j-1][0] & (a[i]==b[j])
           |dp[i-1][j-1][1] & (a[i]==b[j])
//a[i-1]与b[0~j-1]匹配了,且a[i]实际要与b[j]做匹配

dp[i][j][2]=dp[i-1][j-1][0] & (a[i]==b[j+1])
		   |dp[i-1][j-1][1] & (a[i]==b[j+1])
//a[i-1]与b[0~j-1]匹配了,且a[i]实际要与b[j+1]做匹配

=========================================================================

不过该程序的常数巨大,后来还是被加强加版数据卡得TLE了。
我们再来学习另外一种快些的bitset做法,该做法是把a做bitset压位展开的。

我们开bitset<N>bs[3]
再开bitset<N>w[26]
然后w[i],表示对于字符i,其在a[]串的哪些位置出现。

初始化bs[0][i]都为1,表示匹配b的长度为0的条件下,以i为a[]的匹配起点,都是可以匹配的。
然后我们做m轮次的匹配,每次取出b中的字符。
显然,正常的匹配是这样子的——
bs[nxt]=bs[now]&(w[b[i]]>>i),即以a[]的每个位置为起点,我们查看每个位置往后走i(i∈[0,m))位的字符,是否依然可以匹配到b串,
不过还存在了交换的匹配方式——
bs[nxt]=bs[pre]&(w[b[i]]>>(i-1))&(w[b[i-1]]>>i),即考虑交换的匹配。

【时间复杂度&&优化】
O(nm/32)

*/

【HDU5745 2016 Multi-University Training Contest 2L】【bitset高效做法】La Vie en rose 目标串多少子串可以被原始串做相邻交换得到

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 1e5 + 10, M = 5000, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n, m;
char a[N], b[N], ans[N];
bitset<N>dp[3];
bitset<N>w[26];
int main()
{
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d%d", &n, &m);
		scanf("%s%s", a, b);
		for (int i = 0; i < 26; ++i)w[i].reset();
		for (int i = 0; i < n; ++i)w[a[i] - 'a'][i] = 1;
		dp[0].set();
		dp[1].reset();
		dp[2].reset();
		for (int i = 0; i < m; ++i)
		{
			dp[(i + 1) % 3] = dp[i % 3] & (w[b[i] - 'a'] >> i);
			if (i)
			{
				dp[(i + 1) % 3] |= dp[(i + 2) % 3] & (w[b[i] - 'a'] >> i - 1) &(w[b[i - 1] - 'a'] >> i);
			}
		}
		for (int i = 0; i < n; ++i)ans[i] = '0' + dp[m % 3][i]; ans[n] = 0;
		puts(ans);
	}
	return 0;
}
/*
【trick&&吐槽】
这题竟然暴力就够AC了,惊了个呆!

【题意】
给你一个长度为n(1e5)的目标字符串a
我们有一个长度为m(min(5000,n))的基础串b,
对于这个目标字符串,显然有n-m+1个长度为m的子串
问你,在这些子串中,有哪些子串,是可以通过对基础串一些相邻位置的交换,得到的。

即,我们可以选出若干个基础串的若干个位置p[],使得p[i+1]>p[i]+1,
然后我们swap(p[i],p[i]+1),就可以实现变形后的基础串与目标字符串的匹配

【类型】
暴力 or bitset

【分析】
尽管暴力可过,但是正解是bitset,我们依然需要学习一下——
这题的暴力做法,其实本身就比较相似于一个DP。

这道题定义f[i][j][k]表示——
a串匹配到位点a[i]
b串匹配到位点b[j]
j这个位置的匹配状态为k(0表示b[j]要与b[j-1]交换,1表示没有交换,2表示b[j+1]交换)

转移方程是这个样子的——
dp[i][j][0]=dp[i-1][j-1][2] & (a[i]==b[j-1])
//a[i-1]与b[0~j-1]匹配了,且a[i]实际要与b[j-1]做匹配

dp[i][j][1]=dp[i-1][j-1][0] & (a[i]==b[j])
|dp[i-1][j-1][1] & (a[i]==b[j])
//a[i-1]与b[0~j-1]匹配了,且a[i]实际要与b[j]做匹配

dp[i][j][2]=dp[i-1][j-1][0] & (a[i]==b[j+1])
|dp[i-1][j-1][1] & (a[i]==b[j+1])
//a[i-1]与b[0~j-1]匹配了,且a[i]实际要与b[j+1]做匹配

=========================================================================

不过该程序的常数巨大,后来还是被加强加版数据卡得TLE了。
我们再来学习另外一种快些的bitset做法,该做法是把a做bitset压位展开的。

我们开bitset<N>bs[3]
再开bitset<N>w[26]
然后w[i],表示对于字符i,其在a[]串的哪些位置出现。

初始化bs[0][i]都为1,表示匹配b的长度为0的条件下,以i为a[]的匹配起点,都是可以匹配的。
然后我们做m轮次的匹配,每次取出b中的字符。
显然,正常的匹配是这样子的——
bs[nxt]=bs[now]&(w[b[i]]>>i),即以a[]的每个位置为起点,我们查看每个位置往后走i(i∈[0,m))位的字符,是否依然可以匹配到b串,
不过还存在了交换的匹配方式——
bs[nxt]=bs[pre]&(w[b[i]]>>(i-1))&(w[b[i-1]]>>i),即考虑交换的匹配。

【时间复杂度&&优化】
O(nm/32)

*/


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值