linux cron实战:定时github

相关文章:GitHub第一步:生成ssh keys

最近由于需要使用实验室的一台高性能工作站来跑程序,ssh进行远程登录,但是有时候电脑重启后,ip发生变化,于是在思考用什么办法来存储这台电脑的ip地址。

刚开始的想法是把ip地址直接上传到这个博客,定时更新。后来想想,既然是自己自定义cron任务,能不能上传到github呢?这样就不用浪费博客的流量了,也是进行了下面的实验:

一、编写shell脚本

首先是确定我要如何更新,更新一些什么信息:

最简单的是,需要更新ip和时间,于是我写了个sh脚本,使用date和ifconfig来获取时间和ip信息,最后将两个文件合并,如下:

#use the 'date' command to get the date, and output to the 'tmp1' file
#使用date命令获取时间,并重点向输出到文件tmp1
date > tmp1

#use the 'ifconfig' command to get ip information(just get the ip address include '172',such as 172.168.12.12), and outpt to the 'tmp2' file
#使用ifconfig获取172字段的信息,因为局域网的字段为172开头,避免泄漏不必要的信息,只grep了这部分,同样重定向输出到tmp2
ifconfig | grep 172 > tmp2

#use the 'cat' command to merger 2 files
#使用cat 命令合并两个文件
cat tmp1 tmp2 > result

其中,我们的局域网网段是172开头的,所以直接grep 172。

接着,我尝试在sh脚本里面加上git的操作,整个cron.sh脚本如下:

#!/bin/bash

#open the directory
#打开cron目录
cd ~/cron

#to avoid the new version changed by other users,git pull first
#避免其他用户更新了目录,先进行git pull到endIdx
git pull

#open the directory again
#再次打开目录
cd ~/cron

#use the 'date' command to get the date, and output to the 'tmp1' file
#使用date命令获取时间,并重点向输出到文件tmp1
date > tmp1

#use the 'ifconfig' command to get ip information(just get the ip address include '172',such as 172.168.12.12), and outpt to the 'tmp2' file
#使用ifconfig获取172字段的信息,因为局域网的字段为172开头,避免泄漏不必要的信息,只grep了这部分,同样重定向输出到tmp2
ifconfig | grep 172 > tmp2

#use the 'cat' command to merger 2 files
#使用cat 命令合并两个文件
cat tmp1 tmp2 > result

#add cron.sh and the result file to the git cache
#把cron.sh和result文件添加到git缓冲区
git add cron.sh
git add result

#git commit the change
#提交更改
git commit -m "refresh"

#push to the github
#最后,上传到github
git push

把cron.sh的权限更改为可执行:

chmod +x cron.sh

然后运行:

./cron.sh

发现能够正常工作。

二、添加cron任务

首先我们可以通过

crontab -l

列出当前的crontab文件内容,即看看是否有cron任务。

接着,我们通过:

crontab -e

命令编辑crontab 文件,添加以下内容:

# crontab -l #list the cron jobs
# crontab -e #to edit the cron
# add these text to the crontab,then save and quit
# it will run cron.sh every 15 minutes
# :wq
# it will be better to restart cron service
# ubuntu:  sudo /etc/init.d/cron restart
# centos:  sudo /sbin/service crond restart

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name  command to be executed
*/15 *  *  *  *  /bin/bash ~/cron/cron.sh

其中,关键在于最后一行:

*/15 *  *  *  *  /bin/bash ~/cron/cron.sh

意思是每15分钟执行一次~/cron/cron.sh

保存退出后,为了保险起见,最好重启一下cron服务:

# ubuntu:  
sudo /etc/init.d/cron restart
# centos:  
sudo /sbin/service crond restart

注意ubuntu和centos的命令有些许不同,至此,我们的自动github更新ip程序完成!

通过这个程序,我们还可以定时github其他的信息,既能延长github的最长contributions时间,又可以免费使用github的服务器帮我们存储信息。

项目网址:

https://github.com/siukwan/cron

在日夜运行的实验室服务器ndcRobot:

https://github.com/ndcRobot/cron

cmake项目工程中的头文件与库连接

1.当项目变得庞大时,我们需要划分头文件和功能模块的cpp文件。

2.例如在unp编程中,我把第六章的模块划分为如下:(项目源码网址为:https://github.com/siukwan/unix

模块

 

其中func_err.cpp和func_wrap.cpp包含了常用的函数,这些函数在下面的tcpcli.cpp和tcpserv.cpp等文件中需要使用,这两个文件均#include”func.h”

而tcpcli.cpp和tcpserv.cpp也包含了#include”func.h”。那么tcpcli.cpp和tcpserv.cpp如何能够调用func_err.cpp和func_wrap.cpp里面的函数呢?

首先,因为均包含了公共头文件func.h,所以我们需要在func.h中extern一些常用的函数,如下:

extern

 

这些函数的定义在func_err.cpp和func_wrap.cpp里面,extern只是告诉编译器,编译的时候需要从其他文件中搜索这些函数的定义。

但是tcpcli.cpp和tcpserv.cpp没有直接包含func_err.cpp和func_wrap.cpp,编译的时候找不到这些函数怎么办?

接下来就是需要使用到库和连接了,以下以tcpcli.cpp和func_err.cpp举例。tcpcli.cpp虽然没有定义func_err中的函数,但是因为包含了func.h头文件,这些函数被声明了,这是,我们不能把tcpcli.cpp编译成执行文件,但是可以先把tcpcli.cpp和func_err.cpp编程成.o文件,然后把这两个.o文件连接起来生成执行文件。

CMakeList中的写法如下:

#设置错误函数和包裹函数
SET(LIB_FUNC_ERR_SRC func_err.cpp)
SET(LIB_FUNC_WRAP_SRC func_wrap.cpp)

#设置服务器编译文件
SET(SERVER_SRC_LIST tcpserv.cpp)

#连接库
ADD_LIBRARY(FuncErr STATIC ${LIB_FUNC_ERR_SRC} )
ADD_LIBRARY(FuncWrap STATIC ${LIB_FUNC_WRAP_SRC} )

#输出可执行文件
ADD_EXECUTABLE(server ${SERVER_SRC_LIST})
TARGET_LINK_LIBRARIES(server FuncErr FuncWrap)

MacOS终端中使用cmake

由于最近在mac上面编写c++程序,使用了终端、vim和cmake。

下面是安装cmake的一些记录。

首先,我们需要安装homebrew,可以通过下面的命令进行安装:

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

中间需要使用到超级用户,按照提示输入密码即可。

然后可以继续通过下面的命令安装cmake:

brew install make

我先前是下载了cmake的gui版本,但是可惜gui版本不能在终端中运行,于是按照上述方法安装了终端版本的cmake。

brew是一个安装辅助软件,类似ubuntu里面的apt-get和centos里面的yum,能够通过它下载很多软件,例如opencv等等。

直线与圆的位置关系

直线和圆

现在有一条直线和一个圆。请写一个程序判断它们的位置关系。其中0代表直线和圆相离,1代表两者相切,2代表两者相交。

给定直线上的两个点(不重合)的坐标x1,y1,x2,y2,另外给定圆心坐标xc,yc和圆的半径r。请返回判断的结果(0或1或2)。

测试样例:
-1,-1,1,1,0,0,2
返回:2
时间限制:
1秒
内存限制:
32768KB

AC代码:

class LineAndCircle {
public:

	double cmpzero(double x)
	{
		double eps = 1e-9;
		return fabs(x)<eps ? 0 : x;
	}
    //(x1,y1)和(x2,y2)是线段的两个点, (xc,yc)是圆心,r是半径
	int judgeStat(double x1, double y1, int x2, int y2, int xc, int yc, int r) {
		set<pair<double, double>> p;
		double MIN_VALUE = 1e-8;  //根据需要调整这个值
		double X0 = xc, Y0 = yc;
		double X1 = x1, Y1 = y1;
		double X2 = x2, Y2 = y2;
		double R = r;
		double DX = X2 - X1, DY = Y2 - Y1;
		double A = DX*DX + DY*DY;
		double B = 2 * DX*(X1 - X0) + 2 * DY*(Y1 - Y0);
		double C = (X1 - X0)*(X1 - X0) + (Y1 - Y0)*(Y1 - Y0) - R*R;
		double DELTA = B*B - 4 * A*C;
		int num = 0;
		if (cmpzero(DELTA)>= 0)
		{
			double T1 = (-B - sqrt(DELTA)) / (2 * A);
			double T2 = (-B + sqrt(DELTA)) / (2 * A);
			p.insert({ X1 + T1*DX, Y1 + T1*DY });
			p.insert({ X1 + T2*DX, Y1 + T2*DY });
		}
		return p.size();
	}
};

C++11新特性:智能指针与lambda表达式

1.智能指针

说到智能指针,我们不能不提到C++里面的动态内存管理。在C++里,我们通常使用new来动态分配内存,用delete来释放内存。这两个操作都需要我们自己写到合适的地方,但这样会存在一个问题:假如在new了之后在delete之前抛出异常,那么我们的代码很有可能就没有执行delete操作,从而导致new创建的内存一直无法释放。

那么怎么解决这个问题呢?

智能指针就是用来解决这个问题的,智能指针和常规的指针最重要的区别就是,它能够负责自动释放内存,其它方面和常规的指针一样。这么一来,就可以避免上面的问题了。

share_ptr是用来创建指针,结合以前的new用法:share_ptr<int> tmp=new int(10);

那么我们怎么知道share_ptr的对象是否还存在呢?我们通过weak_ptr,weak_ptr相当于一个工具,用来配合share_ptr使用,可以读取share_ptr的计数器值,当读取到的值为0时,share_ptr的对象也就不存在了。

 

2.lambda表达式

lambda表达式能够把一些函数直接写成表达式,无须函数名字,例如我们使用sort函数自定义比较时:

需要定义cmp函数,并调用cmp函数,但是假如使用lambda表达式,我们就无需要把比较的表达式封装成函数,如下:

sort(array.begin(),array.end(),[](const int&a,const int&b){return a>b;});

这样,我们就不需要因为自定义比较而特意去定义一个函数并给它起名字。

问题:求1到n这n个整数间的异或值,即 1 xor 2 xor 3 … xor n

我们记
QQ截图20151227163719
为a到b之间所有数的异或(包括a和b)。

那么我们先来考虑

QQ截图20151227163919,一共是2^k个数。这2^k个数中,二进制中最高位为1的是第k+1位,且所有2^k个数的最高位均为1。

 

我们考虑k>=1,则2^k为偶数,最高位异或后为0,把这个数的最高位去掉,并不会影响QQ截图20151227163919的值,所以公式1

所以:QQ截图20151227164135
QQ截图20151227164150

对于QQ截图20151227164214,设n的最高位1是在第k+1位(k>=2),

QQ截图20151227164249,对于2^k到n一共m=n+1-2^k个数,最高位共有m=n+1-2^k个1。
(1)当n为奇数时,m=n+1-2^k为偶数,根据公式1,可以进一步化为:QQ截图20151227164359 由于n-2^k和n都是奇数,递推上面的公式,可以得到QQ截图20151227164514

为什么会得到这个公式?QQ截图20151227164359相当于把n去掉其最高位,经过多轮递推后,n只剩下两个位,为什么剩下两个位?因为k>=2,所以剩下的两位无法再进行递推。又因为n为奇数,所以n不是1就是3,写成通项公式的话,就是QQ截图20151227164514

所以最终有:
QQ截图20151227164607

(2)当n为偶数时,m=n+1-2^k为奇数,则最高位共有奇数m=n+1-2^k个1,QQ截图20151227164650,最后和2^k相或,是因为最高位有奇数个1。

经过递推,我们可以知道,n二进制表示中,原来是1的位,依然是1,原来为0的位,依然为0,因为每次递推都会把当时最高位1保留下来,因为k>=2,实则相当于把n的前k-2为保留下来。

n的二进制表示为:QQ截图20151227164751n’记为:QQ截图20151227164822

,即把n的最后两位清零, 那么上述公式可以化为:

QQ截图20151227164840
即当n%4=0时,即n=n’,所以QQ截图20151227164906当n%4=2时,即n=n’+2,所以QQ截图20151227164940

综上所述:QQ截图20151227164958

 

 

#1043 : 完全背包

题目:
1.实则为一个完全背包问题。
2.使用动态规划求解:
dp[i][j]表示从前i个物品开始选择,重量不超过j时,背包的最大价值。
dp[n][m]为最终答案。
状态转移方程为:

//背包空余重量不足
if (weight[i]>j) dp[i + 1][j] = dp[i][j];
//动态规划,选和不选的情况
else
dp[i + 1][j] = max(dp[i][j], dp[i+1][j – weight[i]] + value[i]);

注意是dp[i+1][j – weight[i]]

3.写了两个版本,其中一个是没有优化的,一个是优化的版本。其中需要注意:
(1)优化版本只采用dp[j]
(2)因为dp[i + 1][j] = max(dp[i][j], dp[i+1][j – weight[i]] + value[i])需要使用到dp[i+1][j-weight[i]]的值,所以在遍历重量的时候需要从前面开始遍历:
for (int j = 0; j <= m; j++)
这是和01背包不同的地方!

时间限制:20000ms
单点时限:1000ms
内存限制:256MB

描述

且说之前的故事里,小Hi和小Ho费劲心思终于拿到了茫茫多的奖券!而现在,终于到了小Ho领取奖励的时刻了!

等等,这段故事为何似曾相识?这就要从平行宇宙理论说起了………总而言之,在另一个宇宙中,小Ho面临的问题发生了细微的变化!

小Ho现在手上有M张奖券,而奖品区有N种奖品,分别标号为1到N,其中第i种奖品需要need(i)张奖券进行兑换,并且可以兑换无数次,为了使得辛苦得到的奖券不白白浪费,小Ho给每件奖品都评了分,其中第i件奖品的评分值为value(i),表示他对这件奖品的喜好值。现在他想知道,凭借他手上的这些奖券,可以换到哪些奖品,使得这些奖品的喜好值之和能够最大。

提示一: 切,不就是0~1变成了0~K么

提示二:强迫症患者总是会将状态转移方程优化一遍又一遍

提示三:同样不要忘了优化空间哦!

输入

每个测试点(输入文件)有且仅有一组测试数据。

每组测试数据的第一行为两个正整数N和M,表示奖品的种数,以及小Ho手中的奖券数。

接下来的n行描述每一行描述一种奖品,其中第i行为两个整数need(i)和value(i),意义如前文所述。

测试数据保证

对于100%的数据,N的值不超过500,M的值不超过10^5

对于100%的数据,need(i)不超过2*10^5, value(i)不超过10^3

输出

对于每组测试数据,输出一个整数Ans,表示小Ho可以获得的总喜好值。

样例输入
5 1000
144 990
487 436
210 673
567 58
1056 897
样例输出
5940

AC代码:

/*
题目:
1.实则为一个完全背包问题。
2.使用动态规划求解:
dp[i][j]表示从前i个物品开始选择,重量不超过j时,背包的最大价值。
dp[n][m]为最终答案。
状态转移方程为:

//背包空余重量不足
if (weight[i]>j) dp[i + 1][j] = dp[i][j];
//动态规划,选和不选的情况
else
dp[i + 1][j] = max(dp[i][j], dp[i+1][j - weight[i]] + value[i]);

注意是dp[i+1][j - weight[i]] 

3.写了两个版本,其中一个是没有优化的,一个是优化的版本。其中需要注意:
(1)优化版本只采用dp[j]
(2)因为dp[i + 1][j] = max(dp[i][j], dp[i+1][j - weight[i]] + value[i])需要使用到dp[i+1][j-weight[i]]的值,所以在遍历重量的时候需要从前面开始遍历:
for (int j = 0; j <= m; j++)
这是和01背包不同的地方!

*/
/*
*/

#include<string>
#include <iomanip>
#include<fstream>
#include<set>
#include<queue>
#include<map>
//#include<unordered_set>
//#include<unordered_map>
//#include <sstream>
//#include "func.h"
//#include <list>
#include<stdio.h>
#include<iostream>
#include<string>
#include<memory.h>
#include<limits.h>
//#include<stack>
#include<vector>
#include <algorithm>
using namespace std;

/*
函数名  :main
函数功能:主函数
*/
int main(void)
{
	
	//int n, m;
	//cin >> n >> m;
	//vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
	//vector<int> value(n, 0);
	//vector<int> weight(n, 0);
	//for (int i = 0; i < n; i++)
	//{
	//	scanf("%d %d", &weight[i], &value[i]);
	//}

	//for (int i = 0; i < n; i++)
	//{
	//	for (int j = 0; j <= m; j++)
	//	{
	//		//背包空余重量不足
	//		if (weight[i]>j) dp[i + 1][j] = dp[i][j];
	//		//动态规划,选和不选的情况
	//		else
	//			dp[i + 1][j] = max(dp[i][j], dp[i+1][j - weight[i]] + value[i]);
	//	}
	//}

	//cout << dp[n][m] << endl;
	//

	//优化版本:
	int n, m;
	cin >> n >> m;
	vector<int> dp(m + 1, 0);
	vector<int> value(n, 0);
	vector<int> weight(n, 0);
	for (int i = 0; i < n; i++)
	{
		scanf("%d %d", &weight[i], &value[i]);
	}

	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j <= m; j++)
		{//因为dp[j-weight[i]]是引用了前面的数组,所以需要从后面开始遍历
			//背包空余重量不足
			if (weight[i]>j) dp[j] = dp[j];
			//动态规划,选和不选的情况
			else
				dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
		}
	}

	cout << dp[m] << endl;


	return  0;
}

#1038 : 01背包

题目:
1.实则为一个01背包问题。
2.使用动态规划求解:
dp[i][j]表示从前i个物品开始选择,重量不超过j时,背包的最大价值。
dp[n][m]为最终答案。
状态转移方程为:

//背包空余重量不足
if (weight[i]>j) dp[i + 1][j] = dp[i][j];
//动态规划,选和不选的情况
else
dp[i + 1][j] = max(dp[i][j], dp[i][j – weight[i]] + value[i]);

3.写了两个版本,其中一个是没有优化的,一个是优化的版本。其中需要注意:
(1)优化版本只采用dp[j]
(2)因为dp[i + 1][j] = max(dp[i][j], dp[i][j – weight[i]] + value[i])需要使用到dp[i][j-weight[i]]的值,所以在遍历重量的时候需要从后面开始遍历:
for (int j = m; j >= 0; j–)

时间限制:20000ms
单点时限:1000ms
内存限制:256MB

描述

且说上一周的故事里,小Hi和小Ho费劲心思终于拿到了茫茫多的奖券!而现在,终于到了小Ho领取奖励的时刻了!

小Ho现在手上有M张奖券,而奖品区有N件奖品,分别标号为1到N,其中第i件奖品需要need(i)张奖券进行兑换,同时也只能兑换一次,为了使得辛苦得到的奖券不白白浪费,小Ho给每件奖品都评了分,其中第i件奖品的评分值为value(i),表示他对这件奖品的喜好值。现在他想知道,凭借他手上的这些奖券,可以换到哪些奖品,使得这些奖品的喜好值之和能够最大。

提示一:合理抽象问题、定义状态是动态规划最关键的一步

提示二:说过了减少时间消耗,我们再来看看如何减少空间消耗

输入

每个测试点(输入文件)有且仅有一组测试数据。

每组测试数据的第一行为两个正整数N和M,表示奖品的个数,以及小Ho手中的奖券数。

接下来的n行描述每一行描述一个奖品,其中第i行为两个整数need(i)和value(i),意义如前文所述。

测试数据保证

对于100%的数据,N的值不超过500,M的值不超过10^5

对于100%的数据,need(i)不超过2*10^5, value(i)不超过10^3

输出

对于每组测试数据,输出一个整数Ans,表示小Ho可以获得的总喜好值。

样例输入
5 1000
144 990
487 436
210 673
567 58
1056 897
样例输出
2099

AC代码:

/*
题目:
1.实则为一个01背包问题。
2.使用动态规划求解:
dp[i][j]表示从前i个物品开始选择,重量不超过j时,背包的最大价值。
dp[n][m]为最终答案。
状态转移方程为:

//背包空余重量不足
if (weight[i]>j) dp[i + 1][j] = dp[i][j];
//动态规划,选和不选的情况
else
dp[i + 1][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);

3.写了两个版本,其中一个是没有优化的,一个是优化的版本。其中需要注意:
(1)优化版本只采用dp[j]
(2)因为dp[i + 1][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i])需要使用到dp[i][j-weight[i]]的值,所以在遍历重量的时候需要从后面开始遍历:
for (int j = m; j >= 0; j--)

*/
/*
*/

#include<string>
#include <iomanip>
#include<fstream>
#include<set>
#include<queue>
#include<map>
//#include<unordered_set>
//#include<unordered_map>
//#include <sstream>
//#include "func.h"
//#include <list>
#include<stdio.h>
#include<iostream>
#include<string>
#include<memory.h>
#include<limits.h>
//#include<stack>
#include<vector>
#include <algorithm>
using namespace std;

/*
函数名  :main
函数功能:主函数
*/
int main(void)
{
	/*
	int n, m;
	cin >> n >> m;
	vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
	vector<int> value(n, 0);
	vector<int> weight(n, 0);
	for (int i = 0; i < n; i++)
	{
		scanf("%d %d", &weight[i], &value[i]);
	}

	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j <= m; j++)
		{
			//背包空余重量不足
			if (weight[i]>j) dp[i + 1][j] = dp[i][j];
			//动态规划,选和不选的情况
			else
				dp[i + 1][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);
		}
	}

	cout << dp[n][m] << endl;
	*/

	//优化版本:
	int n, m;
	cin >> n >> m;
	vector<int> dp(m + 1, 0);
	vector<int> value(n, 0);
	vector<int> weight(n, 0);
	for (int i = 0; i < n; i++)
	{
		scanf("%d %d", &weight[i], &value[i]);
	}

	for (int i = 0; i < n; i++)
	{
		for (int j = m; j >= 0; j--)
		{//因为dp[j-weight[i]]是引用了前面的数组,所以需要从后面开始遍历
			//背包空余重量不足
			if (weight[i]>j) dp[j] = dp[j];
			//动态规划,选和不选的情况
			else
				dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
		}
	}

	cout << dp[m] << endl;


	return  0;
}

#1107 : Shortest Proper Prefix

题目:
1.题目涉及到了一个proper prefix和shortest proper prefix。
2.proper prefix是指真前缀,即包含这个前缀的单词小于等于5个。
3.shortest proper prefix是指最短真前缀,是指除了它之外的前缀(可以理解为比它长的前缀)都不是真前缀。
所以我们要求出这样一个集合:
(1)集合中任意前缀对应的单词数量小于等于5;
(2)对于集合中任意前缀p,p的扩展前缀不属于该集合。

这是一道Trie的题目,需要建立Trie树。
我采用了map<char,Trie*> child的结构存储子节点。
在建立树的过程中,通过单词个数,最后进行深度搜索(或者广度搜索),找出<=5的节点的个数,需要注意的是,如果节点的count<=5,则不再遍历它的儿子节点,否则需要遍历它的儿子节点。

问题所在:
1.为了提高速度,我是用了char数组和scanf进行输入。
2.之前我分配的char数组大小为1000、10000、100000,均会出现Runtime Error(RE错误)
3.然后我把代码放到hiho第七十八周的挑战赛上,发现能够得分60,但是仍然RE错误,证明部分测试用例是正确的。
4.接着我把char数组改成string输入,再提交,发现得分能够到达70分,但是错误已经变成TLE,超时错误。
5.于是我开始测试是不是char数组大小不满足要求,把数组大小改为1000000,即100万的长度,终于AC了。

 

时间限制:10000ms
单点时限:1000ms
内存限制:512MB

描述

14214822852319.png

Query auto-completion(QAC) is widely used in many search applications. The basic idea is that when you type some string s in the search box several high-frequency queries which have s as a prefix are suggested. We say string s1 has string s2 as a prefix if and only if the first |s2| characters of s1 are the same as s2 where |s2| is the length of s2.

These days Little Hi has been working on a way to improve the QAC performance. He collected N high-frequency queries. We say a string s is a proper prefix if there are no more than 5 collected queries have s as a prefix. A string s is a shortest proper prefix if s is a proper prefix and all the prefixes of s(except for s itself) are not proper prefixes. Little Hi wants to know the number of shortest proper prefixes given N collected queries.

Hint: the 4 shortest proper prefixes for Sample Input are “ab”, “bb”, “bc” and “be”. Empty string “” is not counted as a proper prefix even if N <= 5.

输入

The first line contains N(N <= 10000), the number of collected queries.

The following N lines each contain a query.

Each query contains only lowercase letters ‘a’-‘z’.

The total length of all queries are no more than 2000000.

Input may contain identical queries. Count them separately when you calculate the number of queries that have some string as a prefix.

输出

Output the number of shortest proper prefixes.

样例输入
12
a
ab
abc
abcde
abcde
abcba
bcd
bcde
bcbbd
bcac
bee
bbb
样例输出
4

AC代码:

/*
题目:
1.题目涉及到了一个proper prefix和shortest proper prefix。
2.proper prefix是指真前缀,即包含这个前缀的单词小于等于5个。
3.shortest proper prefix是指最短真前缀,是指除了它之外的前缀(可以理解为比它长的前缀)都不是真前缀。
所以我们要求出这样一个集合:
(1)集合中任意前缀对应的单词数量小于等于5;
(2)对于集合中任意前缀p,p的扩展前缀不属于该集合。

这是一道Trie的题目,需要建立Trie树。
我采用了map<char,Trie*> child的结构存储子节点。
在建立树的过程中,通过单词个数,最后进行深度搜索(或者广度搜索),找出<=5的节点的个数,需要注意的是,如果节点的count<=5,则不再遍历它的儿子节点,否则需要遍历它的儿子节点。

问题所在:
1.为了提高速度,我是用了char数组和scanf进行输入。
2.之前我分配的char数组大小为1000、10000、100000,均会出现Runtime Error(RE错误)。
3.然后我把代码放到hiho第七十八周的挑战赛上,发现能够得分60,但是仍然RE错误,证明部分测试用例是正确的。
4.接着我把char数组改成string输入,再提交,发现得分能够到达70分,但是错误已经变成TLE,超时错误。
5.于是我开始测试是不是char数组大小不满足要求,把数组大小改为1000000,即100万的长度,终于AC了。

*/
/*
*/

#include<string>
#include <iomanip>
#include<fstream>
#include<set>
#include<queue>
#include<map>
//#include<unordered_set>
//#include<unordered_map>
//#include <sstream>
//#include "func.h"
//#include <list>
#include<stdio.h>
#include<iostream>
#include<string>
#include<memory.h>
#include<limits.h>
//#include<stack>
#include<vector>
#include <algorithm>
using namespace std;

struct Trie{
	map<char, Trie*> child;
	int count;
	Trie() :count(0){};
	Trie(int x) :count(x){};
};

void InsertWord(char *a, int pos, Trie*p)
{
	if (a[pos] == 0) return;//已经遍历完毕
	else
	{
		if (p->child.find(a[pos]) != p->child.end())
		{//找到有这个字母
			p->child[a[pos]]->count++;
			InsertWord(a, pos + 1, p->child[a[pos]]);
		}
		else
		{//没找到,则新插入一个
			p->child[a[pos]] = new Trie(1);//默认count为1
			InsertWord(a, pos + 1, p->child[a[pos]]);
		}
	}
}
void dfs(int &ans, Trie* root)
{
	if (root->count <= 5)
		ans++;
	else if (root->child.size())
	{
		for (map<char, Trie*>::iterator ite = root->child.begin(); ite != root->child.end(); ite++)
		{
			dfs(ans, ite->second);
		}
	}
}
/*
函数名  :main
函数功能:主函数
*/
int main(void)
{
	int n;
	Trie *root = new Trie;
	root->count = INT_MAX;
	char *word = new char[1000000];
	memset(word, 0, 1000000);
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		memset(word, 0, 1000000);
		scanf("%s", word);
		InsertWord(word, 0, root);
	}
	int ans = 0;
	dfs(ans, root);

	cout << ans << endl;

	return  0;
}

#1223 : 不等式

题目:

1.刚开始看得时候没有什么思路,后来看了一下C的范围,发现可以遍历所有情况。

2.因为C的范围是0到1000,最多有50条等式,所以采用遍历每个数值,看其满足的等式条数,最终取最大值。

3.注意我们遍历的时候,不能直接遍历0,1,2…;因为出现等式X > 2和X < 3,X=2.5能够同时满足要求。

4.遍历的时候使用0.5的步进进行判断。

5.输入的时候,cin>>string>>string>>int即可。

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

给定n个关于X的不等式,问最多有多少个成立。

每个不等式为如下的形式之一:

X < C

X <= C

X = C

X > C

X >= C

输入

第一行一个整数n。

以下n行,每行一个不等式。

数据范围:

1<=N<=50,0<=C<=1000

输出

一行一个整数,表示最多可以同时成立的不等式个数。

样例输入
4
X = 1
X = 2
X = 3
X > 0
样例输出
2

AC代码:

/*
题目:
1.因为C的范围是0到1000,最多有50条等式,所以采用遍历每个数值,看其满足的等式条数,最终取最大值。
2.注意我们遍历的时候,不能直接遍历0,1,2,3,4,5;因为出现等式X > 2和X < 3,X=2.5能够同时满足要求。
3.遍历的时候使用0.5的步进进行判断。

*/
/*

测试用例:

2
X > 2
X < 3
正确答案:
2


6
X = 1
X = 2
X = 3
X > 0
X >= 0
X <= 0
正确答案:
3

*/

#include<string>
#include <iomanip>
#include<fstream>
#include<set>
#include<queue>
#include<map>
//#include<unordered_set>
//#include<unordered_map>
//#include <sstream>
//#include "func.h"
//#include <list>
#include<stdio.h>
#include<iostream>
#include<string>
#include<memory.h>
#include<limits.h>
//#include<stack>
#include<vector>
#include <algorithm>
using namespace std;
bool cal(float&x, pair<string, int>&e)
{
	string op = e.first;
	int c = e.second;
	if (op == "<")
		return x < c;
	else if (op == "<=")
		return x <= c;
	else if (op == ">")
		return x > c;
	else if (op == ">=")
		return x >= c;
	else
		return x == c;
	
}
/*
函数名  :main
函数功能:主函数
*/
int main(void)
{

	int n;
	cin >> n;
	vector<pair<string, int>> equation(n);
	string x;
	for (int i = 0; i < n; i++)
	{
		cin >> x >> equation[i].first >> equation[i].second;
	}
	int answer = 0;
	//遍历0到1000
	for (float x = -0.5; x <= 1000.5; x+=0.5)
	{
		int tmp = 0;
		//遍历所有的等式,求出满足的条数
		for (int i = 0; i < n; i++)
		{
			tmp += cal(x, equation[i]);
		}
		answer = max(tmp, answer);
	}
	cout << answer << endl;
	return  0;
}