maninbule / contests Goto Github PK
View Code? Open in Web Editor NEW各算法网站的比赛做题记录以及题解
各算法网站的比赛做题记录以及题解
先对数组进行从小到大排序,两个元素的最小差就一定是排序后的某两个相邻两个元素的差。
所以,先遍历排序后的数组找到最小差,然后再遍历一遍数组,将差值为最小差的元素对放入vector里面就好,并且已经是排好序的
class Solution {
public:
vector<vector<int>> minimumAbsDifference(vector<int>& arr) {
vector<vector<int>> res;
sort(arr.begin(),arr.end());
int dif = (int)1e9+7;
for(int i = 1;i<arr.size();i++){
dif = min(dif,arr[i]-arr[i-1]);
}
for(int i = 1;i<arr.size();i++){
if(arr[i]-arr[i-1] == dif){
vector<int> ve = {arr[i-1],arr[i]};
res.push_back(ve);
}
}
return res;
}
};
对于一个x,可以通过容斥原理来判断他以及之前有多少个丑数
之前丑数的个数 = int cnt = x/a+x/b+x/c-x/ab-x/ac-x/bc+x/abc;
ab为a、b的最小公倍数
这样就可以通过二分来算出第n各丑数
typedef long long ll;
class Solution {
public:
ll a,b,c,ab,ac,bc,abc,n;
bool judge(int x){
int cnt = x/a+x/b+x/c-x/ab-x/ac-x/bc+x/abc;
if(cnt<n) return false;
return true;
}
ll lcm(ll a,ll b){
return a*b/__gcd(a,b);
}
int nthUglyNumber(int n1, int a1, int b1, int c1) {
n = n1,a = a1,b = b1,c = c1;
ab = lcm(a,b),ac = lcm(a,c),bc = lcm(b,c),abc = lcm(c,ab);
ll l = 1,r = 2e9+10,mid;
while(l<r){
mid = (l+r)/2;
if(judge(mid)){
r = mid;
}else{
l = mid+1;
}
}
return l;
}
};
假如可以互换的元素对有[2,3],[3,5] 那么[2,3,5]都是可以互换的,他们同属于一个联通块
所以我们需要使用并查集找出所有的联通块,对每个联通块里对应的字母进行排序,然后把排序后的字符串拷贝回原来的位置就行了。
const int maxn = 1e6+10;
class Solution {
public:
int fa[maxn];
map<int,vector<int>> ve;
string str;
int find(int x){
if(x != fa[x])
fa[x] = find(fa[x]);
return fa[x];
}
void join(int x,int y){
int fx = find(x),fy = find(y);
if(fx!=fy) fa[fx] = fy;
}
void solve(int id){
string s = "";int idx = 0;
for(const auto &i:ve[id]){
s+= str[i];
}
sort(s.begin(),s.end());
for(const auto &i:ve[id]){
str[i] = s[idx++];
}
}
string smallestStringWithSwaps(string s, vector<vector<int>>& pairs) {
str = s;
for(int i = 0;i<s.size();i++) fa[i] = i;
for(const auto &v:pairs){
join(v[0],v[1]);
}
for(int i = 0;i<s.size();i++) find(i);
for(int i = 0;i<s.size();i++){
if(ve.count(fa[i]) == 0) ve[fa[i]] = vector<int>();
ve[fa[i]].push_back(i);
}
for(int i = 0;i<s.size();i++){
if(ve[i].size() == 0) continue;
solve(i);
}
return str;
}
};
这题需要多次拓扑排序,而我只会一次。实在不会写,之后变强了再补
对于R这个式子,要R最小,那么分母就应该最大,让分母最大,那么让1/Ri
的分母尽可能的小,数量尽可能多。本题的主要任务就是去找一个连续素数的乘积最大但小于等于n,证明如下图片
因为要让分母尽可能小,那么素数从最小的2开始,然后依次乘以下一个素数,直到乘积最大但<=n时,此时就满足了让1/Ri
的分母尽可能的小,数量尽可能多。且这些质数的乘积组合都是小于等于n的因子
关于大数,使用了一个封装好的大数模板,里面有加减乘除取余,还加入了运算符重载,虽然代码量挺大,但是的确好用
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stdio.h>
#include <cstring>
#include <string>
# define FOR(i, a, b) for(int i = a; i <= b; i++)
# define _FOR(i, a, b) for(int i = a; i >= b; i--)
using namespace std;
const int maxn = 1e6+10;
typedef long long ll;
struct BigInt
{
static const int M = 1000;
int num[M + 10], len;
BigInt(int x) {
clean();
itoBig(x);
}
BigInt() {
clean();
}
void clean(){
memset(num, 0, sizeof(num));
len = 1;
}
void read(){
char str[M + 10];
scanf("%s", str);
len = strlen(str);
FOR(i, 1, len)
num[i] = str[len - i] - '0';
}
void write(){
_FOR(i, len, 1)
printf("%d", num[i]);
//puts("");
}
void itoBig(int x){
clean();
while(x != 0){
num[len++] = x % 10;
x /= 10;
}
if(len != 1) len--;
}
bool operator < (const BigInt &cmp) const {
if(len != cmp.len) return len < cmp.len;
_FOR(i, len, 1)
if(num[i] != cmp.num[i]) return num[i] < cmp.num[i];
return false;
}
bool operator > (const BigInt &cmp) const { return cmp < *this; }
bool operator <= (const BigInt &cmp) const { return !(cmp < *this); }
bool operator != (const BigInt &cmp) const { return cmp < *this || *this < cmp; }
bool operator == (const BigInt &cmp) const { return !(cmp < *this || *this < cmp); }
BigInt operator + (const BigInt &A) const {
BigInt S;
S.len = max(len, A.len);
FOR(i, 1, S.len){
S.num[i] += num[i] + A.num[i];
if(S.num[i] >= 10){
S.num[i] -= 10;
S.num[i + 1]++;
}
}
while(S.num[S.len + 1]) S.len++;
return S;
}
BigInt operator - (const BigInt &A) const {
BigInt S;
S.len = max(len, A.len);
FOR(i, 1, S.len){
S.num[i] += num[i] - A.num[i];
if(S.num[i] < 0){
S.num[i] += 10;
S.num[i + 1]--;
}
}
while(!S.num[S.len] && S.len > 1) S.len--;
return S;
}
BigInt operator * (const BigInt &A) const {
BigInt S;
if((A.len == 1 && A.num[1] == 0) || (len == 1 && num[1] == 0)) return S;
S.len = A.len + len - 1;
FOR(i, 1, len)
FOR(j, 1, A.len){
S.num[i + j - 1] += num[i] * A.num[j];
S.num[i + j] += S.num[i + j - 1] / 10;
S.num[i + j - 1] %= 10;
}
while(S.num[S.len + 1]) S.len++;
return S;
}
BigInt operator / (const BigInt &A) const {
BigInt S;
if((A.len == 1 && A.num[1] == 0) || (len == 1 && num[1] == 0)) return S;
BigInt R, N;
S.len = 0;
_FOR(i, len, 1){
N.itoBig(10);
R = R * N;
N.itoBig(num[i]);
R = R + N;
int flag = -1;
FOR(j, 1, 10){
N.itoBig(j);
if(N * A > R){
flag = j - 1;
break;
}
}
S.num[++S.len] = flag;
N.itoBig(flag);
R = R - N * A;
}
FOR(i, 1, S.len / 2) swap(S.num[i], S.num[len - i + 1]);
while(!S.num[S.len] && S.len > 1) S.len--;
return S;
}
BigInt operator % (const BigInt &A) const {
BigInt S;
BigInt P = *this / A;
S = *this - P * A;
return S;
}
};
int T;
BigInt N,zero(0);
vector<int> p;
bool vis[maxn];
void init(){
int n = 1e6;
for(int i =2;i<n/i;i++){
if(!vis[i]){
p.push_back(i);
for(int j = 2*i;j<n;j+=i){
vis[j] = true;
}
}
}
}
BigInt gcd(BigInt a,BigInt b){
return b == zero? a:gcd(b,a%b);
}
int main(){
init();
cin>>T;
while(T--){
N.read();
BigInt sum(1),son(1),mu(1);
for(int i = 0;i<p.size();i++) {
BigInt pi(p[i]), pi_1(p[i] + 1);
sum = sum * pi;
if (sum > N) break;
son = son * pi_1;
mu = mu * pi;
}
BigInt g = gcd(son,mu);
son = son/g,mu = mu/g;
swap(son,mu);
son.write();putchar('/');mu.write();putchar('\n');
}
return 0;
}
得出结论:
当数轴两端选点数相同时,也就是奇数次选点,选最左端未被选过的点,以让下次偶数选点获得的增值最大。所以奇数选点选未选点最左的一个,偶数选点选未选点最右的一个
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 1e6+10;
typedef long long ll;
ll p[maxn];
ll res[maxn];
int main(){
int T;cin>>T;
while(T--){
int N;cin>>N;
ll dis;
for(int i = 2;i<=N;i++){
scanf("%lld",&dis);
p[i] = p[i-1]+dis;
}
res[1] = 0,res[2] = p[N];//初始化
int l = 2,r = N-1,idx = 3;//
int tl = 1,tr = 1;ll suml = 0,sumr = 0;//左右前缀的数量,左右前缀和
while(l<=r){
if(idx%2 == 1){ //奇数选左边端点,偶数选右边
ll sum = (p[l]-p[1])*tl-suml + (p[N]-p[l])*tr-sumr;
res[idx] = res[idx-1]+sum;
tl++,suml+=p[l],l++;
}else{
ll sum = (p[r]-p[1])*tl-suml + (p[N]-p[r])*tr-sumr;
res[idx] = res[idx-1]+sum;
tr++,sumr+=p[N]-p[r],r--;
}
idx++;
}
for(int i = 1;i<=N;i++){
printf("%lld",res[i]);
if(i!=N)putchar(' ');
}
puts("");
}
return 0;
}
先把平年的每月的天数写进数组,计算出天数,之后需要特判一下是否是闰年并且月份>=3,是的话就天数再+1
class Solution {
public:
int d[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
int dayOfYear(string date) {
int days = 0;
int year = stoi(date.substr(0,4)); // 讲字串转换成int
int mouth = stoi(date.substr(5,2));
int day = stoi(date.substr(8,2));
if(((year%4 ==0 && year%100!=0) || (year%400 == 0) )&& mouth>=3) days++; //判断闰年+1的情况
for(int i = 1;i<mouth;i++) days += d[i];
days += day;
return days;
}
};
我们定义DP[i][j]: 投了i个骰子,得分为j的方法数
假如f = 3
dp[0][0] = 1
则状态转移方程为:dp[i][j] = dp[i-1][j-1] + dp[i-1][j-2] + dp[i-1][j-3]
用三重循环就可以实现出来
class Solution {
public:
typedef long long ll;
const ll mod = 1e9+7;
ll dp[50][2000];
int numRollsToTarget(int d, int f, int target) {
dp[0][0] = 1;
for(int i = 1;i<=d;i++){
for(int j = 1;j<=target;j++){
for(int k = 1;k<=f;k++){
if(j-k<0) continue;
dp[i][j] = (dp[i][j]+dp[i-1][j-k])%mod;
}
}
}
return dp[d][target];
}
};
先map记录各字母出现的总次数,然后定义一个L,R 从左到右把数组扫一遍就行了。
以 "aaababa"为例:起点为L = R= 0,R扫到第二个b停止,此时扫过的长度为R-L;
然后 L = R = 第一个b的位置,再继续扫
也就是说,从一个起点开始扫,遇到第一个不等于起点元素的进行跳过,第二次不等于起点元素或者已经扫到数组末尾了就计算此次扫过的长度并更新最大长度。注意如果扫过的元素个数大于map中记录的,需要减一。
class Solution {
public:
int maxRepOpt1(string text) {
unordered_map<char,int> mp;
for(auto c:text) mp[c]++;
int l = 0,r = 0,newi = 0,len = text.length();
int res = 0;
for(int i = 0;i<len;i = newi){
int tag = 0;
for(int j = i;j<=len;j++){
if(j == len || (text[i] != text[j] && tag == 1)){ //扫一次的结束条件
int t = j-i;
if(t > mp[text[i]]) t--;
res = max(res,t);
if(j == len) newi = len;//特判
break;
}
if(text[i]!=text[j] && tag == 0) tag = 1,newi = j; //第一次遇到不等于第一个元素的时候
}
}
return res;
}
};
先基础各个元素出现的次数,然后再对出现的次数进行计数,若有2个以上出现次数相同就为假,否则为真
ps: [-1000,1000] 可以加1000 映射到[0,2000],这样做的原因是数组下标为非负值
class Solution {
public:
int coun[2500],coun2[2500];
bool uniqueOccurrences(vector<int>& arr) {
for(const auto & v:arr) coun[v+1000]++; //元素出现的次数
for(int i = 0;i<=2000;i++) {
if(coun[i] ==0) continue;
coun2[coun[i]]++; //出现的次数的次数
if(coun2[coun[i]]>1) return false;
}
return true;
}
};
先假定一个开始坐标L,然后看他最大的结束位置R(剩余可用开销最小);然后重复(L-1,看R是否可以继续向右移动) 。这样就可以遍历完所有剩余可用开销最小的区间
例如: 1 2 3 4,L = 1,R = 4, len = R-L = 3
然后去掉1,剩余可用开销增大|s[1]-t[1]|
,然后R看是否能用剩余可用开销继续向右移动
class Solution {
public:
int equalSubstring(string s, string t, int maxCost) {
int len = s.length(),res = 0,cur = maxCost;
int i = 0,j = 0;
for(;i<len;i++){
while(j<len){
if(s[j] == t[j]){ //相等不用开销
j++;
}else if(cur - (int)abs(s[j]-t[j]) < 0){ //剩余可用开销已达到最小
break;
}else{
cur -= (int)abs(s[j]-t[j]);
j++;
}
}
res = max(res,j-i);
cur += (int)abs(s[i]-t[i]);//去掉开头,增加可用开销
}
return res;
}
};
用两个同步的栈,一个记录元素值,一个记录对应元素出现的次数。当栈顶元素出现的次数等于k,两个栈同时pop次数k。
我这题用的数组模拟栈,因为可以进行遍历,比较方便。
class Solution {
public:
int sk[1000010],coun[1000010],top = -1;
string removeDuplicates(string s, int k) {
for(const auto &c:s){
if(top == -1 || sk[top] != c){
sk[++top] = c;
coun[top] = 1;
}else if(sk[top] == c){
sk[++top] = c;coun[top] = coun[top-1]+1;
}
if(coun[top] == k) for(int i = 1;i<=k;i++) top--;
}
string res = "";
for(int i = 0;i<=top;i++) res+=sk[i];
return res;
}
};
第一次遇见带方向的BFS题,看leetcode题解上有人用的DP,其实差不多的
这题就是在普通的BFS上加了一些特定的移动规则,显得代码特别多,还有一个坑点就是对于一个坐标是可以访问两次的,一个是蛇横向访问,一个是蛇纵向访问。普通的BFS记录步数的数组是dp[x][y]
,但是这里需要再定义一个方向,dp[x][y][0]
表示横向访问(x,y)的步数,dp[x][y][1]
表示纵向访问(x,y)的步数
struct node{
int x,y,st;
};
class Solution {
public:
int dp[110][100][2];//0水平 1竖直
int N;
vector<vector<int>> G;
bool judge(int x,int y,int st){
if(x<0 || x>=N || y<0 || y>=N || dp[x][y][st] != 0 || G[x][y] !=0) return false;
return true;
}
int minimumMoves(vector<vector<int>>& grid) {
N = grid.size(); G = grid;
queue<node> q;
q.push({0,1,0});
dp[0][1][0] = 0;
while(q.size()){
node cur = q.front();q.pop();
int x,y,st = cur.st;
//rotate
if(st == 0){
x = cur.x+1,y = cur.y-1;
if(judge(x,y,1) && grid[cur.x+1][cur.y] == 0){
q.push({x,y,1});
dp[x][y][1] = dp[cur.x][cur.y][st]+1;
}
}else{
x = cur.x-1,y = cur.y+1;
if(judge(x,y,0) && grid[cur.x][cur.y+1] == 0){
q.push({x,y,0});
dp[x][y][0] = dp[cur.x][cur.y][st]+1;
}
}
//move
if(st == 0){
x = cur.x,y = cur.y+1; // right
if(judge(x,y,st)){
q.push({x,y,st});
dp[x][y][st] = dp[cur.x][cur.y][st]+1;
}
x = cur.x+1,y = cur.y; //down
if(judge(x,y,st) && grid[x][y-1] == 0){
q.push({x,y,st});
dp[x][y][st] = dp[cur.x][cur.y][st]+1;
}
}else{
x = cur.x+1,y = cur.y; //down
if(judge(x,y,st) ){
q.push({x,y,st});
dp[x][y][st] = dp[cur.x][cur.y][st]+1;
}
x = cur.x,y = cur.y+1; //right
if(judge(x,y,st)&& grid[x-1][y] == 0){
q.push({x,y,st});
dp[x][y][st] = dp[cur.x][cur.y][st]+1;
}
}
}
return dp[N-1][N-1][0] == 0? -1:dp[N-1][N-1][0];
}
};
先任意选两个点求出一个斜率用最简分数a/b
来表示,约分需要使用到gcd。然后遍历坐标数组,如果存在两个相邻的元素的斜率c/d
不等于a/b
就返回false
,否则就true
注意所有分数都必须是最简的,0/12
最简分数为0/1
class Solution {
public:
int gcd(int a,int b){
return !b? a:gcd(b,a%b);
}
bool checkStraightLine(vector<vector<int>>& c) {
int son = c[1][0]-c[0][0],mu = c[1][1]-c[0][1],g = gcd(son,mu);
son/=g,mu/=g;
for(int i = 1;i<c.size();i++){
int s = c[i][0]-c[i-1][0],m = c[i][1]-c[i-1][1],g = gcd(s,m);
s/=g,m/=g;
if(s!=son || m!=mu) return false;
}
return true;
}
};
先把字符串的从小到大排序,对于一个字符串,遍历这个字符串,判断他的每一个前缀是否出现过,若出现过就舍弃,若没有就加入到集合中
假如有/a/b
已经在集合中了,因为/a/b/c/d
的前缀a/b
已经在集合中了,所以就舍弃。
/a/b/c/d
的前缀有/a
,/a/b
,/a/b/c
class Solution {
public:
vector<string> removeSubfolders(vector<string>& f) {
set<string> st; vector<string> res;
sort(f.begin(),f.end());
for(const auto&s:f){
int exist = 0;
for(int i = 0;i<s.length();i++){
if(s[i] == '/' && st.count(s.substr(0,i))){
exist = 1;
break;
}
}
if(!exist){
st.insert(s);
res.push_back(s);
}
}
return res;
}
};
因为答案是一个长度问题,在[0,字符串长度]之间,所以可以尝试用二分来解决,关键是judge
函数比较难写,其实也不难,就是一个滑动窗口。
假如已经有一个窗口在字符串中了,窗口**有t
个字符,窗口外Q、W、E、R
各有a、b、c、d
个.设abcd中最大值为m
,dif
为abcd与m
的差值总和,当t>=dif
时,此时的窗口大小是可以的,也就是说此次二分的mid
是可以替换出来平衡字符串的。
所以我们的目的就了找长度最小的合法窗口,合法窗口指的是可以替换出平衡字符串
class Solution {
public:
string str;
unordered_map<char,int> mp;
bool judge(int n){
unordered_map<char,int> mp2;
mp2['Q'] = mp['Q'];mp2['W'] = mp['W'];mp2['E'] = mp['E'];mp2['R'] = mp['R'];
for(int i = 0;i<n;i++) mp2[str[i]]--;
int mx = max(max(mp2['Q'],mp2['W']),max(mp2['E'],mp2['R']));
int dif = 0; for(auto m:mp2) dif+= mx-m.second;
if(n>=dif) return true;
for(int i = n;i<str.length();i++){
mp2[str[i-n]]++;mp2[str[i]]--;
int mx = max(max(mp2['Q'],mp2['W']),max(mp2['E'],mp2['R']));
int dif = 0; for(auto m:mp2) dif+= mx-m.second;
if(n>=dif) return true;
}
return false;
}
int balancedString(string s) {
str = s;
for(auto c:s) mp[c]++;
int l = 0,r = s.length(),mid;
while(l<r){
mid = (l+r)/2;
if(judge(mid)) r = mid;
else l = mid+1;
}
return l;
}
};
先把开始时间、结束时间、收益装进一个结构体,按照结束时间从小到大排序。
dp[i]表示第i天的收益。假如一个开始时间s,结束时间e,或者收益w,那么可以想到dp[e] = max(dp[e],dp[s]+w)
但是有可能dp[s] = 0,但是dp[t] (t<s) 不为0,所以我们需要用二分查找第最后一个小于等于s的结束时间t,
然后dp[e] = max(dp[e],dp[t]+w)
。
struct work{
int s,e,w;
};
bool cmp(const work & n1,const work & n2){
if(n1.e != n2.e) return n1.e < n2.e;
return n1.s<n2.s;
}
class Solution {
public:
int len;
unordered_map<int,int> dp;
int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {
len = startTime.size();
vector<work> node(len);
for(int i = 0;i<len;i++){
node[i] = {startTime[i],endTime[i],profit[i]};
}
sort(node.begin(),node.end(),cmp);
sort(endTime.begin(),endTime.end());
for(int i = 0;i<len;i++){
if(i>=1) dp[node[i].e] = dp[node[i-1].e];
int t = upper_bound(endTime.begin(),endTime.end(),node[i].s) - endTime.begin();
if(--t>=0){
dp[node[i].e] = max(dp[node[i].e],dp[endTime[t]]+node[i].w);
}else{
dp[node[i].e] = max(dp[node[i].e],dp[node[i].s]+node[i].w);
}
}
return dp[endTime[len-1]];
}
};
刺裸裸的签到题
#include <iostream>
using namespace std;
int main(){
int T;cin>>T;
while(T--){
int cur,cnt = 0;
for(int i = 0;i<4;i++){
scanf("%d",&cur);
if(cur>0) cnt++;
}
if(cnt == 0) puts("Typically Otaku");
if(cnt == 1) puts("Eye-opener");
if(cnt == 2) puts("Young Traveller");
if(cnt == 3) puts("Excellent Traveller");
if(cnt == 4) puts("Contemporary Xu Xiake");
}
return 0;
}
移动到某个位置偶数步代价为0,奇数步代价为1。所以选定位置时,若选定的是奇数位置,那么代价为所有偶数位置的个数,选定的是偶数位置,那么代价为所有奇数位置的个数。
所有只要记录一下奇数和偶数的个数 ,然后小的那一个就行
class Solution {
public:
int minCostToMoveChips(vector<int>& chips) {
int res[2] = {0,0};
for(auto v:chips) res[v&1]++;
return min(res[0],res[1]);
}
};
从左到右依次遍历,假如当前的元素是x,那么此时更新以x结尾的定差序列 dp[x] = dp[x-dif]+1 (dif 为定差)。在更新过程中不断的取max就行,然后返回max。这个dp的存储可以用unordered_map来存。
class Solution {
public:
int longestSubsequence(vector<int>& arr, int difference) {
unordered_map<int,int> mp;
int res = 0,dif = difference;
for(const auto& v:arr){
mp[v] = mp[v-dif]+1;
res = max(mp[v],res);
}
return res;
}
};
遍历整个面板,对于一个不为0的位置开始DFS,DFS过程中需要加入访问标记+回溯,因为题目中移动过程中不可以回头。
class Solution {
public:
bool vis[100][100];
int dir[4][2] = {-1,0,0,-1,1,0,0,1};
int N,M,res = 0;
vector<vector<int>> grid2;
int getMaximumGold(vector<vector<int>>& grid) {
N = grid.size(),M = grid[0].size(); grid2 = grid;
for(int i = 0;i<grid.size();i++){
for(int j = 0;j<grid[i].size();j++){
if(grid[i][j] != 0){
vis[i][j] = 1;
DFS(i,j,grid[i][j]);
vis[i][j] = 0;
}
}
}
return res;
}
void DFS(int i,int j,int cur){
res = max(res,cur);
for(int d = 0;d<4;d++){
int x = i+dir[d][0],y = j+dir[d][1];
if(x<0 || x>=N || y<0 || y>=M || vis[x][y] == 1 || grid2[x][y] == 0) continue;
vis[x][y]= 1;
DFS(x,y,cur+grid2[x][y]);
vis[x][y]= 0;
}
}
};
和第2题一样,需要知道每个字母可以接在什么字母后面。
例如a可以接在i和u后面,更加一个长度,以a结尾的字符串就等于上长度以e,i,u结尾的个数总和
a: e,i,u e: a,i i: e,o o: i u: i,o
class Solution {
public:
const int mod = 1e9+7;
int countVowelPermutation(int n) {
long long dp[5],f[5],res =0;
for(int i = 0;i<5;i++) dp[i] = 1;//n==1 的时候
while(--n){
f[0] = (dp[1]+dp[2]+dp[4])%mod; //更新dp值
f[1] = (dp[0]+dp[2])%mod;
f[2] = (dp[1]+dp[3])%mod;
f[3] = (dp[2])%mod;
f[4] = (dp[2]+dp[3])%mod;
for(int i = 0;i<5;i++) dp[i] = f[i];//拷贝回去
}
for(int i = 0;i<5;i++) res = (res+dp[i])%mod;//计算总和
return res;
}
};
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.