LeetCode 1160 了解C++的I/O输入输出流加速

  主要是理解一下做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;
}

LeetCode 1160 了解C++的I/O输入输出流加速_第1张图片
  应该是我还没理解到底加速的是谁,算了阅读一下外网的这篇文章程序竞赛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<,endl比较慢,因为它会强制清除stream的缓存,一般来说是没必要的。①原因点我,用’\n’而不是endl就对了。
  我们可以用这个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缓冲区。
  其他两者的不同

  • endl是个运算符,而\n是个字符
  • endl不会占用任何内存空间,而\n是个字符所以会占用1比特
  • endl两边不能加双引号,而\n可以(好像没人在意)
  • C++两者都可用而C只能用\n

看完感觉这应该只是针对OJ的一点奇技淫巧,不过的确很强。

你可能感兴趣的:(LeetCode)