主要是理解一下做Leetcode时遇到的这种奇怪的加速方法。
同样的代码,直接执行是68ms,加速后是36ms,LeetCode1160
LeetCode581效果一样,不加48ms,加了32ms。
只需把代码加在开头
ios::sync_with_stdio(0);
cin.tie(0);
或者
static auto speedup = [](){ ios::sync_with_stdio(false); cin.tie(nullptr); return nullptr; }();
完整的例子
class Solution {
public:
int countCharacters(vector<string>& words, string chars){
int sum = 0;
unsigned int ch[26];
unsigned int tempCh[26];
memset(ch, 0, sizeof(ch));
for (char i : chars)
++ch[i - 'a'];
for (string i : words){
bool flag = true;
for (int i = 0; i < 26; ++i)
tempCh[i] = ch[i];
for (char c : i){
if (tempCh[c - 'a'] == 0){
flag = false;
break;
}
else
--tempCh[c - 'a'];
}
if (flag)
sum += i.length();
}
return sum;
}
};
static int n=[](){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
return 0;
}();
所以WTF?先做一下测试,默认情况下cin和scanf效率对比。
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int DATA_COUNT = 1000000;
vector<int> v(DATA_COUNT, -1);
double cppTestSpeed()
{
clock_t start = clock();
ifstream inFile;
inFile.open("C:\\Users\\MaxLy\\Desktop\\data.txt");
for (int i =0; i < DATA_COUNT; ++i)
inFile >> v[i];
return (double)(clock() - start) / CLOCKS_PER_SEC;
}
double cTestSpeed()
{
clock_t start = clock();
FILE* stream;
freopen_s(&stream, "C:\\Users\\MaxLy\\Desktop\\data.txt", "r", stdin);
for (int i = 0; i < DATA_COUNT; ++i)
scanf_s("%d", &v[i]);
return (double)(clock() - start) / CLOCKS_PER_SEC;
}
void CreateTestData()
{
ofstream outFile;
outFile.open("C:\\Users\\MaxLy\\Desktop\\data.txt");
if (outFile.is_open())
for (int i = 0; i < DATA_COUNT; ++i)
outFile << i << " ";
outFile.close();
}
int main()
{
CreateTestData();
printf("%.3lf\n", cppTestSpeed());
printf("%.3lf\n", cTestSpeed());
return 0;
}
首先结果很明确,cpp的流读入就是没有scanf快
cpp:2.537
c:0.743
然后我把加速的代码加上去了,发现速度并没有什么明显的变化。
double cppTestSpeed()
{
clock_t start = clock();
ifstream inFile;
inFile.open("C:\\Users\\MaxLy\\Desktop\\data.txt");
inFile::sync_with_stdio(0);
inFile.tie(0);
for (int i = 0; i < DATA_COUNT; ++i)
inFile >> v[i];
return (double)(clock() - start) / CLOCKS_PER_SEC;
}
应该是我还没理解到底加速的是谁,算了阅读一下外网的这篇文章程序竞赛IO加速
翻译如下
作者Vinay Garg。
在程序竞赛中,读取输入越快越好。
你肯定已经见过了很多次“Warning: Large I/O data, be careful with certain languages 虽然只要算法好,大部分语言都没问题)””。这些问题的关键就是IO加速技巧。
通常为了速度都推荐用scanf/printf而不是cin/cout。然而,你仍然可以用cin/cout加入如下两行后达到跟scanf/printf同样的速度。
ios_base::sync_with_stdio(false);
如果在程序执行第一次IO操作前调用该加速方法,它就可以开启或关闭所有C++标准流与相关的标准C流的同步。在任何IO操作前写上ios_base::sync_with_stdio (false);
(默认是true)可以避免这种同步。它是std::ios_base对象的静态方法。
cin.tie(NULL);
tie()
方法可以保证cout会在cin接收输入前清除缓存。对于需要稳定更新的可交互的控制台程序非常有用,但它也会被大规模的I/O限制运行速度。NULL参数返回一个空指针。
此外,你可以用以下include语句添加STL。
#include
那么你程序竞赛的模版可以这么写。
#include
using namespace std;
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
return 0;
}
推荐用cout<<"\n";
而不是cout<
我们可以用这个OJ问题来测试一下。
在继续阅读前,我强烈建议先把这个题做了。
正常I/O:以下代码使用了cin和cout。问题AC,时间2.17秒
// A normal IO example code
#include
using namespace std;
int main()
{
int n, k, t;
int cnt = 0;
cin >> n >> k;
for (int i=0; i<n; i++)
{
cin >> t;
if (t % k == 0)
cnt++;
}
cout << cnt << "\n";
return 0;
}
I/O加速:我们可以加两行代码稍微提高速度。以下代码AC时间0.41秒
// A fast IO program
#include
using namespace std;
int main()
{
// added the two lines below
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n, k, t;
int cnt = 0;
cin >> n >> k;
for (int i=0; i<n; i++)
{
cin >> t;
if (t % k == 0)
cnt++;
}
cout << cnt << "\n";
return 0;
}
现在,对于程序竞赛,比如ACM ICPC,Google CodeJam,TopCoder Open,以下是读取整数最快的代码。
void fastscan(int &number)
{
//variable to indicate sign of input number
bool negative = false;
register int c;
number = 0;
// extract current character from buffer
c = getchar();
if (c=='-')
{
// number is negative
negative = true;
// extract the next character from the buffer
c = getchar();
}
// Keep on extracting characters if they are integers
// i.e ASCII Value lies from '0'(48) to '9' (57)
for (; (c>47 && c<58); c=getchar())
number = number *10 + c - 48;
// if scanned input has a negative sign, negate the
// value of the input number
if (negative)
number *= -1;
}
// Function Call
int main()
{
int number;
fastscan(number);
cout << number << "\n";
return 0;
}
getchar_unlocked()程序竞赛快速读取
①翻译
虽然endl和’\n’作用一样,但是也有略微不同。
cout << endl; // Inserts a new line and flushes the stream
cout << "\n"; // Only inserts a new line.
因此,
cout<
与以下等价
cout<<'\n'<
所以cout<<"\n"
看起来效率比cout<,endl;
更高一点,除非你必须清除stream缓冲区。
其他两者的不同
看完感觉这应该只是针对OJ的一点奇技淫巧,不过的确很强。