You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

633 lines
17 KiB

  1. #include <chrono>
  2. #include <fstream>
  3. #include <iostream>
  4. #include <memory>
  5. #include <sstream>
  6. #include <string>
  7. #include <unordered_map>
  8. /*
  9. * Amirlan Sharipov BS21-01
  10. * https://codeforces.com/profile/RinRi
  11. * I've copied most of the comments in this code from the assignment
  12. * specification
  13. */
  14. #define PRIMENUM1 1000007
  15. #define PRIMENUM2 1003337
  16. #define MAXN 50001
  17. #define MINN 8
  18. using std::chrono::duration;
  19. using std::chrono::duration_cast;
  20. using std::chrono::high_resolution_clock;
  21. using std::chrono::milliseconds;
  22. template <class T>
  23. class ICircularBoundedQueue {
  24. public:
  25. virtual void offer(const T &value) = 0; // insert an element to the rear of
  26. // the queue overwrite the oldest
  27. // elements when the queue is full
  28. virtual T &poll() = 0; // remove an element from the front of the queue
  29. virtual T &peek() const = 0; // look at the element at the front of the
  30. // queue (without removing it)
  31. virtual void flush() = 0; // remove all elements from the queue
  32. virtual bool isEmpty() const = 0; // is the queue empty?
  33. virtual bool isFull() const = 0; // is the queue full?
  34. virtual int size() const = 0; // number of elements
  35. virtual int capacity() const = 0; // maximum capacity
  36. };
  37. template <class T>
  38. class ArrayCircularBoundedQueue : ICircularBoundedQueue<T> {
  39. public:
  40. ArrayCircularBoundedQueue<T>(int maxSize) {
  41. mxsz = maxSize;
  42. data = new T *[mxsz]();
  43. }
  44. ~ArrayCircularBoundedQueue<T>() {
  45. for (int i = 0; i < mxsz; ++i) delete data[i];
  46. delete[] data;
  47. }
  48. // time complexity: O(1) in any case
  49. void offer(const T &value) {
  50. if (sz < mxsz) {
  51. curPush = (cur + sz) % mxsz;
  52. sz++;
  53. }
  54. data[curPush++] = new T(value);
  55. curPush %= mxsz;
  56. }
  57. // time complexity: O(1) in any case
  58. void offer(T *value) {
  59. if (sz < mxsz) {
  60. curPush = (cur + sz) % mxsz;
  61. sz++;
  62. }
  63. data[curPush++] = value;
  64. curPush %= mxsz;
  65. }
  66. // time complexity: O(1) in any case
  67. T &poll() try {
  68. if (sz == 0) throw "queue is empty!";
  69. // T &ans = *data[cur];
  70. delete data[cur];
  71. data[cur] = NULL;
  72. sz--;
  73. cur++;
  74. cur %= mxsz;
  75. // return ans;
  76. } catch (const char *msg) {
  77. std::cerr << msg << std::endl;
  78. throw;
  79. }
  80. // time complexity: O(1) in any case
  81. T *pollpoint() try {
  82. if (sz == 0) throw "queue is empty!";
  83. // T &ans = *data[cur];
  84. data[cur] = NULL;
  85. sz--;
  86. cur++;
  87. cur %= mxsz;
  88. // return ans;
  89. } catch (const char *msg) {
  90. std::cerr << msg << std::endl;
  91. throw;
  92. }
  93. // time complexity: O(1) in any case
  94. T &peek() const { return *data[cur]; }
  95. T *peekpoint() const { return data[cur]; }
  96. // time complexity: O(n) in any case
  97. void flush() {
  98. while (sz) poll();
  99. }
  100. // time complexity: O(1) in any case
  101. bool isEmpty() const { return !sz; }
  102. // time complexity: O(1) in any case
  103. bool isFull() const { return (sz == mxsz); }
  104. // time complexity: O(1) in any case
  105. int size() const { return sz; }
  106. // time complexity: O(1) in any case
  107. int capacity() const { return mxsz; }
  108. // time complexity: O(n) in any case
  109. void print() {
  110. for (int i = 0; i < sz; ++i) {
  111. // data[(cur + i) % mxsz].list();
  112. std::cout << *data[(cur + i) % mxsz] << '\n';
  113. }
  114. }
  115. void printset() {
  116. for (int i = 0; i < sz; ++i) {
  117. data[(cur + i) % mxsz]->list();
  118. std::cout << '\n';
  119. }
  120. }
  121. private:
  122. T **data;
  123. int sz = 0, mxsz = 0, cur = 0, curPush = 0;
  124. };
  125. template <class T>
  126. class IBoundedStack {
  127. public:
  128. virtual void push(const T &value) = 0; // push an element onto the stack
  129. // remove the oldest element
  130. // when if stack is full
  131. virtual T &pop() = 0; // remove an element from the top of the stack
  132. virtual T &top() const = 0; // look at the element at the top of the stack
  133. // (without removing it)
  134. virtual void flush() = 0; // remove all elements from the stack
  135. virtual bool isEmpty() const = 0; // is the stack empty?
  136. virtual bool isFull() const = 0; // is the stack full?
  137. virtual int size() const = 0; // number of elements
  138. virtual int capacity() const = 0; // maximum capacity
  139. };
  140. template <class T>
  141. class QueuedBoundedStack : IBoundedStack<T> {
  142. public:
  143. QueuedBoundedStack<T>(int size) {
  144. q1 = new ArrayCircularBoundedQueue<T>(size);
  145. q2 = new ArrayCircularBoundedQueue<T>(size);
  146. mxsz = size;
  147. }
  148. ~QueuedBoundedStack<T>() {
  149. delete q1;
  150. delete q2;
  151. }
  152. // time complexity: O(n*m), Theta(n*m). The algorithm's complexity depends
  153. // on the size of q1 as well as the cost of operation of copying. In this
  154. // case(problem C) it is O(n*m), that is why I decided to use pointers
  155. // instead. See function below this one. Worst case O(n), best case O(1),
  156. // avg O(n)
  157. void push(const T &value) {
  158. q2->offer(value);
  159. while (!q1->isEmpty()) {
  160. if (sz == mxsz && q1->size() == 1) {
  161. q1->poll();
  162. break;
  163. }
  164. T &temp = q1->peek();
  165. q2->offer(temp);
  166. q1->poll();
  167. }
  168. if (sz < mxsz) sz++;
  169. ArrayCircularBoundedQueue<T> *temp = q1;
  170. q1 = q2;
  171. q2 = temp;
  172. }
  173. // time complexity: O(n)
  174. void push(T *value) {
  175. q2->offer(value);
  176. while (!q1->isEmpty()) {
  177. if (sz == mxsz && q1->size() == 1) {
  178. q1->poll();
  179. break;
  180. }
  181. T *temp = q1->peekpoint();
  182. q2->offer(temp);
  183. q1->pollpoint();
  184. }
  185. if (sz < mxsz) sz++;
  186. ArrayCircularBoundedQueue<T> *temp = q1;
  187. q1 = q2;
  188. q2 = temp;
  189. }
  190. // time complexity: O(1) in any case (theta(1))
  191. T &pop() try {
  192. if (q1->isEmpty()) throw "q1 is empty!";
  193. // T &ans = q1->peek();
  194. q1->poll();
  195. sz--;
  196. // return ans;
  197. } catch (const char *msg) {
  198. std::cerr << msg << std::endl;
  199. throw;
  200. }
  201. // time complexity: O(1) in any case
  202. T &top() const try {
  203. if (q1->isEmpty()) throw "q1 is empty!";
  204. return q1->peek();
  205. } catch (const char *msg) {
  206. std::cerr << msg << std::endl;
  207. throw;
  208. }
  209. // time complexity: O(1) in any case
  210. void flush() {
  211. sz = 0;
  212. q1->flush();
  213. q2->flush();
  214. }
  215. // time complexity: O(1) in any case
  216. bool isEmpty() const { return !sz; }
  217. // time complexity: O(1) in any case
  218. bool isFull() const { return (sz == mxsz); }
  219. // time complexity: O(1) in any case
  220. int size() const { return sz; }
  221. // time complexity: O(1) in any case
  222. int capacity() const { return mxsz; }
  223. void print() { q1->print(); }
  224. void printset() { q1->printset(); }
  225. private:
  226. ArrayCircularBoundedQueue<T> *q1, *q2;
  227. int sz = 0, mxsz = 0, cur = 0, curPush = 0;
  228. };
  229. template <class T>
  230. class ISet {
  231. virtual void add(const T &item) = 0; // add an item to the set
  232. virtual void remove(T &item) = 0; // remove an item from the set
  233. virtual bool contains(
  234. T &item) const = 0; // check if an item belongs to the set
  235. virtual int size() const = 0; // the number of elements in the set
  236. virtual bool isEmpty() const = 0; // check if the set is empty
  237. };
  238. template <class T>
  239. class DoubleHashSet : ISet<T> {
  240. public:
  241. // TODO: Write comments about every method
  242. enum State : char { EMPTY = 0, DELETED = 1, TAKEN = 2 };
  243. DoubleHashSet<T>() {
  244. sz = 0;
  245. mxsz = 0;
  246. data = 0;
  247. state = 0;
  248. }
  249. DoubleHashSet<T>(int maxSize) {
  250. mxsz = maxSize;
  251. sz = 0;
  252. data = new T *[mxsz]();
  253. state = new State[mxsz]();
  254. }
  255. // O(n) because of copying
  256. DoubleHashSet<T>(const DoubleHashSet<T> &st) {
  257. sz = st.size();
  258. mxsz = st.getMaxSize();
  259. data = new T *[mxsz]();
  260. state = new State[mxsz]();
  261. T **tempdata = st.getData();
  262. State *tempstate = st.getState();
  263. for (int i = 0; i < mxsz; ++i) {
  264. if (tempstate[i] == TAKEN) data[i] = new T(*tempdata[i]);
  265. state[i] = tempstate[i];
  266. }
  267. }
  268. // O(n*m) deleting
  269. ~DoubleHashSet<T>() {
  270. for (int i = 0; i < mxsz; ++i)
  271. if (state[i] == TAKEN) delete data[i];
  272. delete[] data;
  273. delete[] state;
  274. }
  275. // O(n*m) n -> size, m -> size of data
  276. void list() {
  277. std::ios_base::sync_with_stdio(0);
  278. std::cin.tie(0);
  279. std::cout.tie(0);
  280. for (int i = 0; i < mxsz; ++i) {
  281. if (state[i] == TAKEN) std::cout << *data[i] << ' ';
  282. }
  283. }
  284. // Compression function. O(1)
  285. int hash(int h1, int h2, int64_t i) const {
  286. // size_t h1 = hasher(item);
  287. // h1 %= PRIMENUM1;
  288. // size_t h2 = hasher(item);
  289. // h2 %= PRIMENUM2;
  290. return ((int64_t)h1 + i * (int64_t)h2) % (int64_t)mxsz;
  291. // return ((int64_t)hash1(item, PRIMENUM1) + i * (int64_t)hash2(item,
  292. // PRIMENUM2)) % mxsz; return (h1 + i * (int64_t)hash2(item, PRIMENUM2))
  293. // % mxsz;
  294. }
  295. // Hash function n1. O(n)
  296. int hash1(const std::string &item, int prime) const {
  297. int ans = 0, sz = item.size();
  298. int64_t cur = 1;
  299. for (int i = 0; i < item.size(); ++i) {
  300. ans += (cur * (int64_t)item[i]) % prime;
  301. ans %= prime;
  302. cur *= 127;
  303. cur %= prime;
  304. }
  305. return ans;
  306. }
  307. // Hash function n2. O(n)
  308. int hash2(const std::string &item, int prime) const {
  309. int ans = 0, sz = item.size();
  310. int64_t cur = 1;
  311. for (int i = 0; i < item.size(); ++i) {
  312. ans += (cur * (int64_t)item[sz - i - 1]) % prime;
  313. ans %= prime;
  314. cur *= 127;
  315. cur %= prime;
  316. }
  317. return ans;
  318. }
  319. // Add an item to the set O(n^2) in worst case (all colisions). Copying
  320. // takes time as well. Avg and best case: O(n)
  321. void add(const T &item) {
  322. int h1 = hash1(item, PRIMENUM1), h2 = hash2(item, PRIMENUM2);
  323. while (1) {
  324. for (int i = 0; i < mxsz; ++i) {
  325. int pos = hash(h1, h2, i);
  326. if (state[pos] == EMPTY || state[pos] == DELETED) {
  327. data[pos] = new T(item);
  328. state[pos] = TAKEN;
  329. sz++;
  330. return;
  331. } else if (state[pos] == TAKEN) {
  332. if (*data[pos] == item) return;
  333. }
  334. }
  335. int tempmxsz = mxsz;
  336. T **tempdata = data;
  337. State *tempstate = state;
  338. int newsz = std::max(2 * tempmxsz, 8);
  339. data = new T *[newsz]();
  340. state = new State[newsz]();
  341. mxsz = newsz;
  342. sz = 0;
  343. for (int i = 0; i < tempmxsz; ++i) {
  344. if (tempstate[i] == TAKEN) {
  345. add(*tempdata[i]);
  346. }
  347. }
  348. for (int i = 0; i < tempmxsz; ++i)
  349. if (state[i] == TAKEN) delete tempdata[i];
  350. delete[] tempdata;
  351. delete[] tempstate;
  352. }
  353. }
  354. // Remove an item. worst case: O(n*m), avg and best: O(m), where m is size
  355. // of data.
  356. void remove(T &item) try {
  357. int h1 = hash1(item, PRIMENUM1), h2 = hash2(item, PRIMENUM2);
  358. for (int i = 0; i < mxsz; ++i) {
  359. int pos = hash(h1, h2, i);
  360. if (state[pos] == EMPTY) break;
  361. if (state[pos] == TAKEN) {
  362. if (*data[pos] == item) {
  363. delete data[pos];
  364. data[pos] = NULL;
  365. state[pos] = DELETED;
  366. sz--;
  367. return;
  368. }
  369. }
  370. }
  371. throw "specified item doesn't exist!";
  372. } catch (const char *msg) {
  373. std::cerr << msg << std::endl;
  374. throw;
  375. }
  376. // worst case: O(n)
  377. bool contains(T &item) const {
  378. int h1 = hash1(item, PRIMENUM1), h2 = hash2(item, PRIMENUM2);
  379. for (int i = 0; i < mxsz; ++i) {
  380. int pos = hash(h1, h2, i);
  381. if (state[pos] == EMPTY) break;
  382. if (state[pos] == TAKEN)
  383. if (*data[pos] == item) return true;
  384. }
  385. return false;
  386. }
  387. int size() const { return sz; }
  388. bool isEmpty() const { return (sz == 0); }
  389. T **getData() const { return data; }
  390. State *getState() const { return state; }
  391. int getMaxSize() const { return mxsz; }
  392. // worst, avg, best case: O(n*m)
  393. void operator=(const DoubleHashSet<T> &temp) {
  394. for (int i = 0; i < mxsz; ++i)
  395. if (state[i] == TAKEN) delete data[i];
  396. delete[] data;
  397. delete[] state;
  398. sz = temp.size();
  399. mxsz = temp.getMaxSize();
  400. data = new T *[mxsz]();
  401. state = new State[mxsz]();
  402. T **tempdata = temp.getData();
  403. State *tempstate = temp.getState();
  404. for (int i = 0; i < mxsz; ++i) {
  405. if (tempstate[i] == TAKEN) data[i] = new T(*tempdata[i]);
  406. state[i] = tempstate[i];
  407. }
  408. }
  409. private:
  410. int mxsz = 0, sz = 0;
  411. T **data;
  412. State *state;
  413. };
  414. void solveBoundedCommandQueue() {
  415. int n, k;
  416. std::cin >> n >> k;
  417. std::cin.ignore();
  418. std::string s;
  419. ArrayCircularBoundedQueue<std::string> q(k);
  420. for (int i = 0; i < n; ++i) {
  421. getline(std::cin, s);
  422. if (q.size() == k) q.poll();
  423. q.offer(s);
  424. }
  425. q.print();
  426. }
  427. void solveCMDWithoutRollbacks() {
  428. std::ios_base::sync_with_stdio(false);
  429. std::cin.tie(0);
  430. std::cout.tie(0);
  431. int n;
  432. std::cin >> n;
  433. std::cin.ignore();
  434. DoubleHashSet<std::string> files;
  435. for (int i = 0; i < n; ++i) {
  436. std::string cmd, cmdName, fileName;
  437. getline(std::cin, cmd);
  438. std::stringstream s(cmd);
  439. s >> cmdName;
  440. if (cmdName == "LIST") {
  441. // TODO improve LIST command
  442. files.list();
  443. std::cout << '\n';
  444. } else if (cmdName == "NEW") {
  445. s >> fileName;
  446. bool exists = files.contains(fileName);
  447. if (exists) {
  448. std::cout << "ERROR: cannot execute " << cmd << '\n';
  449. } else {
  450. files.add(fileName);
  451. }
  452. } else {
  453. s >> fileName;
  454. bool exists = files.contains(fileName);
  455. if (exists) {
  456. files.remove(fileName);
  457. } else {
  458. std::cout << "ERROR: cannot execute " << cmd << '\n';
  459. }
  460. }
  461. }
  462. }
  463. void solveCMDWithRollbacks() {
  464. std::ios_base::sync_with_stdio(0);
  465. std::cin.tie(0);
  466. std::cout.tie(0);
  467. int n, k;
  468. std::cin >> n >> k;
  469. std::cin.ignore();
  470. QueuedBoundedStack<DoubleHashSet<std::string>> states(k);
  471. DoubleHashSet<std::string> dummy;
  472. states.push(dummy);
  473. for (int i = 0; i < n; ++i) {
  474. std::string cmd, cmdName, fileName;
  475. getline(std::cin, cmd);
  476. std::stringstream s(cmd);
  477. s >> cmdName;
  478. if (cmdName == "LIST") {
  479. // TODO improve LIST command
  480. states.top().list();
  481. std::cout << '\n';
  482. } else if (cmdName == "NEW") {
  483. // auto t1 = high_resolution_clock::now();
  484. DoubleHashSet<std::string> *files =
  485. new DoubleHashSet<std::string>(states.top());
  486. s >> fileName;
  487. bool exists = files->contains(fileName);
  488. std::string similar = (fileName.back() == '/'
  489. ? fileName.substr(0, fileName.size() - 1)
  490. : fileName + "/");
  491. bool existsSimilar = files->contains(similar);
  492. if (exists || existsSimilar) {
  493. std::cout << "ERROR: cannot execute " << cmd << '\n';
  494. delete files;
  495. } else {
  496. files->add(fileName);
  497. states.push(files);
  498. // auto t2 = high_resolution_clock::now();
  499. // auto ms_int = duration_cast<milliseconds>(t2 - t1);
  500. // std::cout << ms_int.count() << "ms\n";
  501. continue;
  502. }
  503. } else if (cmdName == "REMOVE") {
  504. DoubleHashSet<std::string> *files =
  505. new DoubleHashSet<std::string>(states.top());
  506. s >> fileName;
  507. bool exists = files->contains(fileName);
  508. if (exists) {
  509. files->remove(fileName);
  510. states.push(files);
  511. continue;
  512. } else {
  513. std::cout << "ERROR: cannot execute " << cmd << '\n';
  514. delete files;
  515. }
  516. } else if (cmdName == "UNDO") {
  517. int times = 1;
  518. if (cmd != "UNDO") s >> times;
  519. if (states.size() > times) {
  520. while (times--) {
  521. states.pop();
  522. }
  523. } else {
  524. std::cout << "ERROR: cannot execute " << cmd << '\n';
  525. }
  526. } else {
  527. std::cout << "ERROR: cannot execute " << cmd << '\n';
  528. }
  529. }
  530. }
  531. int main() {
  532. // solveBoundedCommandQueue();
  533. // solveCMDWithoutRollbacks();
  534. solveCMDWithRollbacks();
  535. }