目录
动态规划思想
![](https://i-blog.csdnimg.cn/blog_migrate/434eb2fe59ecd5b6bcbf7ffd9060399f.png)
思路:
本题需要用动态规划求解,MCS[i][j]
记录短字符串
s1
前
i
个字符和长字符串
s2
前
j
个字符的最长子串的长度,初始化所有值为
0
。当
s1[i-1] = s2[j-1]
时,
MCS[i][j] = MCS[i - 1][j - 1] + 1
,这里使用一个额外的值
start
来记录最长子串在短字符串
s1
中出现的起始位置,
maxlen
记录当前最长子串的长度,当
MCS[i][j] >
maxlen
时,
maxlen = MCS[i][j]
, 则
start = i - maxlen
;档
s1[i-1] != s2[j-1]
时不需要任何操作,最后获取
substr(start, maxlen)
即为所求
1、转移方程为: 如果遍历到的该位两个字符相等,则此时长度等于两个前一位长度+1,
(dp[i][j]=dp[i−1][j−1]+1),如果遍历到该位时两个字符不相等,则置为0,因为这是子串,必须连续相等,断开要重新开始。
2、
vector<vector<int>> MSC(len1+1,vector<int>(len2+1,0));
解析:
用vector构造二维数组,用len2+1构造行并全部初始化为0,用vector<int>(len2+1,0)(初始化为0的行)构造len1+1个列;即:将整个数组初始化为0
(建议先看第二题)
#include <iostream>
#include<vector>
using namespace std;
string getcomsubstr(string &s1,string &s2)
{
if(s1.size()>s2.size())
swap(s1,s2);//要输出在较短串中最先出现的
int len1=s1.size();
int len2=s2.size();
vector<vector<int>> MSC(len1+1,vector<int>(len2+1,0));
int start=0,maxsize=0;
for(int i=1;i<=len1;i++) //一较短串为基础,才能找较短串中最先出现的
for(int j=1;j<=len2;j++)
{
if(s2[j-1]==s1[i-1])
MSC[i][j]=MSC[i-1][j-1]+1;
if(MSC[i][j]>maxsize)//有更长公共子串,更新长度
{
maxsize=MSC[i][j];
//以i结尾的最大长度max,则起始位置为i-max
start=i-maxsize;
}
}
return s1.substr(start,maxsize);
}
int main()
{
string str1,str2;
while(cin>>str1>>str2)
{
string sub=getcomsubstr(str1,str2);
cout<<sub<<endl;
}
return 0;
}
经典DP
![](https://i-blog.csdnimg.cn/blog_migrate/4dbfba00cfae7bbda221c38533eccdaa.png)
思路:
求最大公共子串,使用递推实现 假设 x(i): 字符串第 i 个字符 y(j): 字符串第 j 个字符 dp[i][j]: 以 x(i),y(j) 结尾的最大子串长度 比如: x: "abcde" y:"bcdae" dp[2][1]: 以 x(2),y(1) 结尾的最大子串长度 即: x 遍历到 "abc", y 遍历 到 "bc", 都以字符 'c' 结尾时最大公共子串为 "bc" 故:当计算 dp[i][j] 时,首先看 x(i),y(j) 的值:( 1 ) : x(i) == y(j) 当前两个字符串结尾的字符相等, dp[i][j] = dp[i-1][j-1] + 1 即个它的长度加 1 (2 ) : x(i) != y(j) 当前两个字符 串结尾的字符不相等,说明没有以这连个字符结尾的公共子串 即 dp[i][j] = 0(3 ) : dp[0][j] 和 dp[i][0] 表示以 某个子串的第一个字符结尾,最大长度为 1 如果 x(0) == y(j) 或者 x(i) == y(0), 则长度为 1 ,否则为 0 当 dp 中的 所有元素计算完之后,从中找打最大的值输出
#include <iostream>
#include<vector>
using namespace std;
int getcomsubstr(string &s1,string &s2)
{
int len1=s1.size();
int len2=s2.size();
vector<vector<int>> MSC(len1+1,vector<int>(len2+1,0));
int start=0,maxsize=0;
for(int i=1;i<=len1;i++)
for(int j=1;j<=len2;j++)
{
if(s2[j-1]==s1[i-1])
MSC[i][j]=MSC[i-1][j-1]+1;
if(MSC[i][j]>maxsize)//有更长公共子串,更新长度
{
maxsize=MSC[i][j];
}
}
return maxsize;
}
int main()
{
string str1,str2;
while(cin>>str1>>str2)
{
int maxv=getcomsubstr(str1,str2);
cout<<maxv<<endl;
}
return 0;
}
简单的数学问题
看到题目字数和样例人麻了?其实只是一道小学奥数题
思路:
每次读取一个数之后,算出他经过
k
次洗牌后的位置,只用一个长度为
2n
数组用来输出
根据当前数的位置,可以算出经过一次洗牌后的位置
- 如果当前数小于等于n(即在左手),则他下次出现的位置是 2*当前位置
- 与之对应的当前位置 + n(即在右手)的牌,则他下次出现的位置是 2*当前位置 + 1
高效率地把一个数组的内容复制给另一个数组:
#include <stdio.h>
#define SIZE 3
int main()
{
int a[SIZE], b[SIZE]={1,2,3};
register int *x, *y;
for( x = a, y = b ; y < &b[SIZE] ; )
*x++ = *y++;
return 0;
}
#include <iostream>
using namespace std;
int main() {
int T,n,k;
cin>>T;
while(T--)
{
cin>>n>>k;
int num=2*n;
int card[num+1];
for(int i=0;i<num;i++) cin>>card[i];
while(k--)
{
int tmp[num+1];
int *x,*y;
for(x=tmp,y=card;y<&card[num+1];) *x++=*y++;
for(int j=0;j<n;j++)
{
card[2*j]=tmp[j]; //左手的牌排放的位置
card[2*j+1]=tmp[j+n];//右手的牌排放的位置
}
}
for(int i=0;i<num-1;i++) cout<<card[i]<<" ";
cout<<card[num-1]<<endl;
}
}
// 64 位输出请用 printf("%lld")